Merge branch 'master' into goodbye-bootstrap
commit
b02a79f0e4
|
@ -1,5 +1,5 @@
|
|||
[bumpversion]
|
||||
current_version = 1.4.1.3
|
||||
current_version = 1.4.2.1
|
||||
files = README.md server/swagger.json
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.(?P<release>\d+)
|
||||
serialize = {major}.{minor}.{patch}.{release}
|
||||
|
|
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -1,8 +1,15 @@
|
|||
## v1.4.2.0 [unreleased]
|
||||
## v1.4.3.0 [unreleased]
|
||||
### Features
|
||||
### UI Improvements
|
||||
### Bug Fixes
|
||||
|
||||
|
||||
## v1.4.2.1 [2018-02-28]
|
||||
### Features
|
||||
1. [#2837](https://github.com/influxdata/chronograf/pull/2837): Prevent execution of queries in cells that are not in view on the dashboard page
|
||||
1. [#2837] (https://github.com/influxdata/chronograf/pull/2837): Prevent execution of queries in cells that are not in view on the dashboard page
|
||||
1. [#2829] (https://github.com/influxdata/chronograf/pull/2829): Add an optional persistent legend which can toggle series visibility to dashboard cells
|
||||
1. [#2829](https://github.com/influxdata/chronograf/pull/2829): Add an optional persistent legend which can toggle series visibility to dashboard cells
|
||||
1. [#2846](https://github.com/influxdata/chronograf/pull/2846): Allow user to annotate graphs via UI or API
|
||||
|
||||
### UI Improvements
|
||||
1. [#2848](https://github.com/influxdata/chronograf/pull/2848): Add ability to set a prefix and suffix on Single Stat and Gauge cell types
|
||||
1. [#2831](https://github.com/influxdata/chronograf/pull/2831): Rename 'Create Alerts' page to 'Manage Tasks'; Redesign page to improve clarity of purpose
|
||||
|
@ -11,6 +18,7 @@
|
|||
1. [#2821](https://github.com/influxdata/chronograf/pull/2821): Save only selected template variable values into dashboards for non csv template variables
|
||||
1. [#2842](https://github.com/influxdata/chronograf/pull/2842): Use Generic APIKey for Oauth2 group lookup
|
||||
1. [#2850](https://github.com/influxdata/chronograf/pull/2850): Fix bug in which resizing any cell in a dashboard causes a Gauge cell to resize
|
||||
1. [#2886](https://github.com/influxdata/chronograf/pull/2886): Don't sort Single Stat & Gauge thresholds when editing threshold values
|
||||
1. [#2851](https://github.com/influxdata/chronograf/pull/2851): Maintain y axis labels in dashboard cells
|
||||
1. [#2819](https://github.com/influxdata/chronograf/pull/2819): Deprecate --new-sources in CLI
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ option.
|
|||
## Versions
|
||||
|
||||
The most recent version of Chronograf is
|
||||
[v1.4.1.3](https://www.influxdata.com/downloads/).
|
||||
[v1.4.2.1](https://www.influxdata.com/downloads/).
|
||||
|
||||
Spotted a bug or have a feature request? Please open
|
||||
[an issue](https://github.com/influxdata/chronograf/issues/new)!
|
||||
|
@ -178,7 +178,7 @@ By default, chronograf runs on port `8888`.
|
|||
To get started right away with Docker, you can pull down our latest release:
|
||||
|
||||
```sh
|
||||
docker pull chronograf:1.4.1.3
|
||||
docker pull chronograf:1.4.2.1
|
||||
```
|
||||
|
||||
### From Source
|
||||
|
|
|
@ -12,10 +12,6 @@ import (
|
|||
// SortTemplates the templates by size, then type, then value.
|
||||
func SortTemplates(ts []chronograf.TemplateVar) []chronograf.TemplateVar {
|
||||
sort.Slice(ts, func(i, j int) bool {
|
||||
if ts[i].Var == ":interval:" {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(ts[i].Values) != len(ts[j].Values) {
|
||||
return len(ts[i].Values) < len(ts[j].Values)
|
||||
}
|
||||
|
@ -63,20 +59,6 @@ func RenderTemplate(query string, t chronograf.TemplateVar, now time.Time) (stri
|
|||
tv[t.Values[i].Type] = t.Values[i].Value
|
||||
}
|
||||
|
||||
if pts, ok := tv["points"]; ok {
|
||||
points, err := strconv.ParseInt(pts, 0, 64)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
dur, err := ParseTime(query, now)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
interval := AutoInterval(points, dur)
|
||||
return strings.Replace(query, t.Var, interval, -1), nil
|
||||
}
|
||||
|
||||
if res, ok := tv["resolution"]; ok {
|
||||
resolution, err := strconv.ParseInt(res, 0, 64)
|
||||
if err != nil {
|
||||
|
@ -101,22 +83,6 @@ func RenderTemplate(query string, t chronograf.TemplateVar, now time.Time) (stri
|
|||
return query, nil
|
||||
}
|
||||
|
||||
func AutoInterval(points int64, duration time.Duration) string {
|
||||
// The function is: ((total_seconds * millisecond_converstion) / group_by) = pixels / 3
|
||||
// Number of points given the pixels
|
||||
pixels := float64(points)
|
||||
msPerPixel := float64(duration/time.Millisecond) / pixels
|
||||
secPerPixel := float64(duration/time.Second) / pixels
|
||||
if secPerPixel < 1.0 {
|
||||
if msPerPixel < 1.0 {
|
||||
msPerPixel = 1.0
|
||||
}
|
||||
return strconv.FormatInt(int64(msPerPixel), 10) + "ms"
|
||||
}
|
||||
// If groupby is more than 1 second round to the second
|
||||
return strconv.FormatInt(int64(secPerPixel), 10) + "s"
|
||||
}
|
||||
|
||||
// AutoGroupBy generates the time to group by in order to decimate the number of
|
||||
// points returned in a query
|
||||
func AutoGroupBy(resolution, pixelsPerPoint int64, duration time.Duration) string {
|
||||
|
|
|
@ -125,38 +125,6 @@ func TestTemplateReplace(t *testing.T) {
|
|||
},
|
||||
want: `SELECT :field: FROM "cpu"`,
|
||||
},
|
||||
{
|
||||
name: "auto interval",
|
||||
query: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(:interval:)`,
|
||||
vars: []chronograf.TemplateVar{
|
||||
{
|
||||
Var: ":interval:",
|
||||
Values: []chronograf.TemplateValue{
|
||||
{
|
||||
Value: "333",
|
||||
Type: "points",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(46702s)`,
|
||||
},
|
||||
{
|
||||
name: "auto interval",
|
||||
query: `SELECT derivative(mean(usage_idle),:interval:) from "cpu" where time > now() - 4320h group by time(:interval:)`,
|
||||
vars: []chronograf.TemplateVar{
|
||||
{
|
||||
Var: ":interval:",
|
||||
Values: []chronograf.TemplateValue{
|
||||
{
|
||||
Value: "333",
|
||||
Type: "points",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: `SELECT derivative(mean(usage_idle),46702s) from "cpu" where time > now() - 4320h group by time(46702s)`,
|
||||
},
|
||||
{
|
||||
name: "auto group by",
|
||||
query: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by :interval:`,
|
||||
|
@ -165,7 +133,7 @@ func TestTemplateReplace(t *testing.T) {
|
|||
Var: ":interval:",
|
||||
Values: []chronograf.TemplateValue{
|
||||
{
|
||||
Value: "999",
|
||||
Value: "1000",
|
||||
Type: "resolution",
|
||||
},
|
||||
{
|
||||
|
@ -175,7 +143,7 @@ func TestTemplateReplace(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
want: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(46702s)`,
|
||||
want: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(46655s)`,
|
||||
},
|
||||
{
|
||||
name: "auto group by without duration",
|
||||
|
@ -185,7 +153,7 @@ func TestTemplateReplace(t *testing.T) {
|
|||
Var: ":interval:",
|
||||
Values: []chronograf.TemplateValue{
|
||||
{
|
||||
Value: "999",
|
||||
Value: "1000",
|
||||
Type: "resolution",
|
||||
},
|
||||
{
|
||||
|
@ -195,7 +163,7 @@ func TestTemplateReplace(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
want: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by time(46702s)`,
|
||||
want: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by time(46655s)`,
|
||||
},
|
||||
{
|
||||
name: "auto group by with :dashboardTime:",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"info": {
|
||||
"title": "Chronograf",
|
||||
"description": "API endpoints for Chronograf",
|
||||
"version": "1.4.1.3"
|
||||
"version": "1.4.2.1"
|
||||
},
|
||||
"schemes": ["http"],
|
||||
"basePath": "/chronograf/v1",
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"transform-runtime",
|
||||
"lodash"
|
||||
],
|
||||
"presets": ["es2015", "react", "stage-0"],
|
||||
"presets": ["env", "react", "stage-0"],
|
||||
"env": {
|
||||
"production": {
|
||||
"plugins": [
|
||||
|
|
14
ui/.eslintrc
14
ui/.eslintrc
|
@ -1,20 +1,26 @@
|
|||
{
|
||||
parser: 'babel-eslint',
|
||||
"parser": "babel-eslint",
|
||||
plugins: [
|
||||
'react',
|
||||
'prettier',
|
||||
'babel',
|
||||
'jest',
|
||||
],
|
||||
extends: [
|
||||
"prettier",
|
||||
"prettier/react"
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
mocha: true,
|
||||
"jest": true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
globals: {
|
||||
expect: true,
|
||||
},
|
||||
"parserOptions": {
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
arrowFunctions: true,
|
||||
binaryLiterals: true,
|
||||
|
@ -244,6 +250,10 @@
|
|||
'semi': false,
|
||||
}],
|
||||
|
||||
// jest
|
||||
'jest/no-disabled-tests': "warn",
|
||||
'jest/no-focused-tests': "error",
|
||||
|
||||
// Babel
|
||||
'babel/no-invalid-this': 1
|
||||
},
|
||||
|
|
|
@ -6,3 +6,4 @@ dist/
|
|||
bower_components/
|
||||
log/
|
||||
.tern-project
|
||||
yarn-error.log
|
|
@ -0,0 +1,16 @@
|
|||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
displayName: 'test',
|
||||
testPathIgnorePatterns: ['/build/'],
|
||||
modulePaths: ['<rootDir>', '<rootDir>/node_modules/'],
|
||||
moduleDirectories: ['src'],
|
||||
setupFiles: ['<rootDir>/test/setupTests.js'],
|
||||
},
|
||||
{
|
||||
runner: 'jest-runner-eslint',
|
||||
displayName: 'lint',
|
||||
testMatch: ['<rootDir>/test/**/*.test.js'],
|
||||
},
|
||||
],
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
var webpack = require('webpack')
|
||||
var path = require('path')
|
||||
const webpack = require('webpack')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
browsers: ['PhantomJS'],
|
||||
singleRun: true,
|
||||
frameworks: ['mocha', 'sinon-chai'],
|
||||
frameworks: ['mocha'],
|
||||
files: [
|
||||
'node_modules/babel-polyfill/dist/polyfill.js',
|
||||
'spec/spec-helper.js',
|
||||
|
@ -42,10 +41,6 @@ module.exports = function(config) {
|
|||
test: /sinon\/pkg\/sinon\.js/,
|
||||
loader: 'imports?define=>false,require=>false',
|
||||
},
|
||||
{
|
||||
test: /\.json$/,
|
||||
loader: 'json',
|
||||
},
|
||||
],
|
||||
},
|
||||
externals: {
|
||||
|
@ -61,7 +56,6 @@ module.exports = function(config) {
|
|||
shared: path.resolve(__dirname, 'src', 'shared'),
|
||||
style: path.resolve(__dirname, 'src', 'style'),
|
||||
utils: path.resolve(__dirname, 'src', 'utils'),
|
||||
sinon: 'sinon/pkg/sinon',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
{
|
||||
"src_folders": ["tests"],
|
||||
"output_folder": "reports",
|
||||
"custom_commands_path": "",
|
||||
"custom_assertions_path": "",
|
||||
"page_objects_path": "",
|
||||
"globals_path": "",
|
||||
|
||||
"selenium": {
|
||||
"start_process": false,
|
||||
"host": "hub-cloud.browserstack.com",
|
||||
"port": 80
|
||||
},
|
||||
|
||||
"live_output" : true,
|
||||
|
||||
"test_settings": {
|
||||
"default": {
|
||||
"selenium_port": 80,
|
||||
"selenium_host": "hub-cloud.browserstack.com",
|
||||
"silent": false,
|
||||
"screenshots": {
|
||||
"enabled": true,
|
||||
"path": "screenshots"
|
||||
},
|
||||
"desiredCapabilities": {
|
||||
"browser": "chrome",
|
||||
"build": "nightwatch-browserstack",
|
||||
"browserstack.user": "${BROWSERSTACK_USER}",
|
||||
"browserstack.key": "${BROWSERSTACK_KEY}",
|
||||
"browserstack.debug": true,
|
||||
"browserstack.local": true,
|
||||
"resolution": "1280x1024"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "chronograf-ui",
|
||||
"version": "1.4.1-3",
|
||||
"version": "1.4.2-1",
|
||||
"private": false,
|
||||
"license": "AGPL-3.0",
|
||||
"description": "",
|
||||
|
@ -9,17 +9,19 @@
|
|||
"url": "github:influxdata/chronograf"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn run clean && env NODE_ENV=production webpack --optimize-minimize --config ./webpack/prodConfig.js",
|
||||
"build:dev": "webpack --config ./webpack/devConfig.js",
|
||||
"start": "yarn run clean && webpack --watch --config ./webpack/devConfig.js",
|
||||
"start:hmr": "webpack-dev-server --open --config ./webpack/devConfig.js",
|
||||
"build": "yarn run clean && webpack --config ./webpack/prod.config.js",
|
||||
"build:dev": "webpack --config ./webpack/dev.config.js",
|
||||
"build:vendor": "webpack --config webpack/vendor.config.js",
|
||||
"start": "yarn run clean && yarn run build:vendor && webpack --watch --config ./webpack/dev.config.js",
|
||||
"start:assets": "webpack --watch --config ./webpack/dev.config.js",
|
||||
"start:hmr": "webpack-dev-server --open --config ./webpack/dev.config.js",
|
||||
"lint": "esw src/",
|
||||
"test": "karma start",
|
||||
"test:integration": "nightwatch tests --skip",
|
||||
"test": "jest --runInBand",
|
||||
"test:lint": "yarn run lint; yarn run test",
|
||||
"test:dev": "concurrently \"yarn run lint --watch\" \"yarn run test --no-single-run --reporters=verbose\"",
|
||||
"clean": "rm -rf build/*",
|
||||
"prettier": "prettier --single-quote --trailing-comma es5 --bracket-spacing false --semi false --write \"{src,spec}/**/*.js\"; eslint src --fix"
|
||||
"test:watch": "jest --watch",
|
||||
"clean": "rm -rf ./build/*",
|
||||
"prettier": "prettier --single-quote --trailing-comma es5 --bracket-spacing false --semi false --write \"{src,spec}/**/*.js\"; eslint src --fix",
|
||||
"eslint-check": "eslint --print-config .eslintrc | eslint-config-prettier-check"
|
||||
},
|
||||
"author": "",
|
||||
"eslintConfig": {
|
||||
|
@ -28,10 +30,16 @@
|
|||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.1.2",
|
||||
"@types/lodash": "^4.14.104",
|
||||
"@types/mocha": "^2.2.48",
|
||||
"@types/node": "^9.4.6",
|
||||
"@types/react": "^16.0.38",
|
||||
"autoprefixer": "^6.3.1",
|
||||
"babel-core": "^6.5.1",
|
||||
"babel-eslint": "6.1.2",
|
||||
"babel-loader": "^6.2.2",
|
||||
"babel-jest": "^22.4.1",
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-plugin-lodash": "^2.0.1",
|
||||
"babel-plugin-syntax-trailing-function-commas": "^6.5.0",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||
|
@ -39,41 +47,38 @@
|
|||
"babel-plugin-transform-react-remove-prop-types": "^0.2.1",
|
||||
"babel-plugin-transform-runtime": "^6.5.0",
|
||||
"babel-polyfill": "^6.13.0",
|
||||
"babel-preset-es2015": "^6.5.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-react": "^6.5.0",
|
||||
"babel-preset-stage-0": "^6.16.0",
|
||||
"babel-runtime": "^6.5.0",
|
||||
"bower": "^1.7.7",
|
||||
"chai": "^3.5.0",
|
||||
"compression-webpack-plugin": "^1.1.8",
|
||||
"concurrently": "^3.5.0",
|
||||
"core-js": "^2.1.3",
|
||||
"css-loader": "^0.23.1",
|
||||
"envify": "^3.4.0",
|
||||
"enzyme": "^2.4.1",
|
||||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-15": "^1.0.5",
|
||||
"eslint": "^3.14.1",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
"eslint-loader": "1.6.1",
|
||||
"eslint-plugin-jest": "^21.12.2",
|
||||
"eslint-plugin-prettier": "^2.1.2",
|
||||
"eslint-plugin-react": "6.6.0",
|
||||
"eslint-watch": "^3.1.2",
|
||||
"express": "^4.14.0",
|
||||
"extract-text-webpack-plugin": "^1.0.1",
|
||||
"file-loader": "^0.8.5",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"file-loader": "^1.1.7",
|
||||
"fork-ts-checker-webpack-plugin": "^0.3.0",
|
||||
"hanson": "^1.1.1",
|
||||
"hson-loader": "^1.0.0",
|
||||
"html-webpack-plugin": "^2.22.0",
|
||||
"html-webpack-include-assets-plugin": "^1.0.2",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"imports-loader": "^0.6.5",
|
||||
"jest": "^22.4.2",
|
||||
"jest-runner-eslint": "^0.4.0",
|
||||
"jsdom": "^9.0.0",
|
||||
"json-loader": "^0.5.4",
|
||||
"karma": "^1.3.0",
|
||||
"karma-cli": "^1.0.1",
|
||||
"karma-mocha": "^1.1.1",
|
||||
"karma-phantomjs-launcher": "^1.0.2",
|
||||
"karma-sinon-chai": "^1.2.4",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-verbose-reporter": "^0.0.6",
|
||||
"karma-webpack": "^1.8.0",
|
||||
"mocha": "^2.4.5",
|
||||
"mocha-loader": "^0.7.1",
|
||||
"json-loader": "^0.5.7",
|
||||
"mustache": "^2.2.1",
|
||||
"node-sass": "^4.5.3",
|
||||
"on-build-webpack": "^0.1.0",
|
||||
|
@ -82,17 +87,21 @@
|
|||
"postcss-loader": "^0.8.0",
|
||||
"postcss-reporter": "^1.3.1",
|
||||
"precss": "^1.4.0",
|
||||
"prettier": "^1.5.3",
|
||||
"prettier": "1.5.3",
|
||||
"react-addons-test-utils": "^15.0.2",
|
||||
"resolve-url-loader": "^1.6.0",
|
||||
"sass-loader": "^3.2.0",
|
||||
"sinon": "^1.17.4",
|
||||
"sinon-chai": "^2.8.0",
|
||||
"react-test-renderer": "^15.6.1",
|
||||
"resolve-url-loader": "^2.2.1",
|
||||
"sass-loader": "^6.0.6",
|
||||
"style-loader": "^0.13.0",
|
||||
"testem": "^1.2.1",
|
||||
"uglify-js": "^2.6.1",
|
||||
"webpack": "^1.13.0",
|
||||
"webpack-dev-server": "^1.14.1"
|
||||
"thread-loader": "^1.1.4",
|
||||
"ts-loader": "^3.5.0",
|
||||
"tslib": "^1.9.0",
|
||||
"typescript": "^2.7.2",
|
||||
"uglifyjs-webpack-plugin": "^1.2.2",
|
||||
"webpack": "^3.11.0",
|
||||
"webpack-bundle-analyzer": "^2.10.1",
|
||||
"webpack-dev-server": "^2.11.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@skidding/react-codemirror": "^1.0.1",
|
||||
|
@ -108,7 +117,7 @@
|
|||
"lodash": "^4.3.0",
|
||||
"moment": "^2.13.0",
|
||||
"nano-date": "^2.0.1",
|
||||
"node-uuid": "^1.4.7",
|
||||
"prop-types": "^15.6.1",
|
||||
"query-string": "^5.0.0",
|
||||
"react": "^15.0.2",
|
||||
"react-addons-shallow-compare": "^15.0.2",
|
||||
|
@ -122,12 +131,12 @@
|
|||
"react-redux": "^4.4.0",
|
||||
"react-router": "^3.0.2",
|
||||
"react-router-redux": "^4.0.8",
|
||||
"react-sparklines": "^1.4.2",
|
||||
"react-tooltip": "^3.2.1",
|
||||
"redux": "^3.3.1",
|
||||
"redux-auth-wrapper": "^1.0.0",
|
||||
"redux-thunk": "^1.0.3",
|
||||
"rome": "^2.1.22",
|
||||
"updeep": "^0.13.0"
|
||||
"updeep": "^0.13.0",
|
||||
"uuid": "^3.2.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
window.then = function(cb, done) {
|
||||
window.setTimeout(function() {
|
||||
cb()
|
||||
if (typeof done === 'function') {
|
||||
done()
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('sinon-chai'))
|
||||
|
||||
global.expect = chai.expect
|
|
@ -1,5 +1,5 @@
|
|||
import _ from 'lodash'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import {
|
||||
getUsers as getUsersAJAX,
|
||||
|
|
|
@ -9,18 +9,13 @@ class ChangePassRow extends Component {
|
|||
this.state = {
|
||||
showForm: false,
|
||||
}
|
||||
this.showForm = ::this.showForm
|
||||
this.handleCancel = ::this.handleCancel
|
||||
this.handleKeyPress = ::this.handleKeyPress
|
||||
this.handleEdit = ::this.handleEdit
|
||||
this.handleSubmit = ::this.handleSubmit
|
||||
}
|
||||
|
||||
showForm() {
|
||||
showForm = () => {
|
||||
this.setState({showForm: true})
|
||||
}
|
||||
|
||||
handleCancel() {
|
||||
handleCancel = () => {
|
||||
this.setState({showForm: false})
|
||||
}
|
||||
|
||||
|
@ -28,12 +23,12 @@ class ChangePassRow extends Component {
|
|||
this.setState({showForm: false})
|
||||
}
|
||||
|
||||
handleSubmit(user) {
|
||||
handleSubmit = user => {
|
||||
this.props.onApply(user)
|
||||
this.setState({showForm: false})
|
||||
}
|
||||
|
||||
handleKeyPress(user) {
|
||||
handleKeyPress = user => {
|
||||
return e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.handleSubmit(user)
|
||||
|
@ -41,7 +36,7 @@ class ChangePassRow extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleEdit(user) {
|
||||
handleEdit = user => {
|
||||
return e => {
|
||||
this.props.onEdit(user, {[e.target.name]: e.target.value})
|
||||
}
|
||||
|
|
|
@ -7,24 +7,20 @@ class QueryRow extends Component {
|
|||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleInitiateKill = ::this.handleInitiateKill
|
||||
this.handleFinishHim = ::this.handleFinishHim
|
||||
this.handleShowMercy = ::this.handleShowMercy
|
||||
|
||||
this.state = {
|
||||
confirmingKill: false,
|
||||
}
|
||||
}
|
||||
|
||||
handleInitiateKill() {
|
||||
handleInitiateKill = () => {
|
||||
this.setState({confirmingKill: true})
|
||||
}
|
||||
|
||||
handleFinishHim() {
|
||||
handleFinishHim = () => {
|
||||
this.props.onKill(this.props.query.id)
|
||||
}
|
||||
|
||||
handleShowMercy() {
|
||||
handleShowMercy = () => {
|
||||
this.setState({confirmingKill: false})
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,9 @@ import {ROLES_TABLE} from 'src/admin/constants/tableSizing'
|
|||
class RoleEditingRow extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleKeyPress = ::this.handleKeyPress
|
||||
this.handleEdit = ::this.handleEdit
|
||||
}
|
||||
|
||||
handleKeyPress(role) {
|
||||
handleKeyPress = role => {
|
||||
return e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.props.onSave(role)
|
||||
|
@ -18,7 +15,7 @@ class RoleEditingRow extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleEdit(role) {
|
||||
handleEdit = role => {
|
||||
return e => {
|
||||
this.props.onEdit(role, {[e.target.name]: e.target.value})
|
||||
}
|
||||
|
|
|
@ -5,12 +5,9 @@ import {USERS_TABLE} from 'src/admin/constants/tableSizing'
|
|||
class UserEditName extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleKeyPress = ::this.handleKeyPress
|
||||
this.handleEdit = ::this.handleEdit
|
||||
}
|
||||
|
||||
handleKeyPress(user) {
|
||||
handleKeyPress = user => {
|
||||
return e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.props.onSave(user)
|
||||
|
@ -18,7 +15,7 @@ class UserEditName extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleEdit(user) {
|
||||
handleEdit = user => {
|
||||
return e => {
|
||||
this.props.onEdit(user, {[e.target.name]: e.target.value})
|
||||
}
|
||||
|
|
|
@ -3,14 +3,7 @@ import React, {Component, PropTypes} from 'react'
|
|||
import {USERS_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
class UserNewPassword extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleKeyPress = ::this.handleKeyPress
|
||||
this.handleEdit = ::this.handleEdit
|
||||
}
|
||||
|
||||
handleKeyPress(user) {
|
||||
handleKeyPress = user => {
|
||||
return e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.props.onSave(user)
|
||||
|
@ -18,7 +11,7 @@ class UserNewPassword extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleEdit(user) {
|
||||
handleEdit = user => {
|
||||
return e => {
|
||||
this.props.onEdit(user, {[e.target.name]: e.target.value})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import AllUsersTableHeader from 'src/admin/components/chronograf/AllUsersTableHeader'
|
||||
import AllUsersTableRowNew from 'src/admin/components/chronograf/AllUsersTableRowNew'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import OrganizationsTableRow from 'src/admin/components/chronograf/OrganizationsTableRow'
|
||||
import OrganizationsTableRowNew from 'src/admin/components/chronograf/OrganizationsTableRowNew'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
import ProvidersTableRow from 'src/admin/components/chronograf/ProvidersTableRow'
|
||||
import ProvidersTableRowNew from 'src/admin/components/chronograf/ProvidersTableRowNew'
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import UsersTableHeader from 'src/admin/components/chronograf/UsersTableHeader'
|
||||
import UsersTableRowNew from 'src/admin/components/chronograf/UsersTableRowNew'
|
||||
|
|
|
@ -20,12 +20,6 @@ import {
|
|||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
|
||||
class QueriesPage extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.updateQueries = ::this.updateQueries
|
||||
this.handleKillQuery = ::this.handleKillQuery
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateQueries()
|
||||
const updateInterval = 5000
|
||||
|
@ -42,7 +36,7 @@ class QueriesPage extends Component {
|
|||
return <QueriesTable queries={queries} onKillQuery={this.handleKillQuery} />
|
||||
}
|
||||
|
||||
updateQueries() {
|
||||
updateQueries = () => {
|
||||
const {source, notify, loadQueries} = this.props
|
||||
showDatabases(source.links.proxy).then(resp => {
|
||||
const {databases, errors} = showDatabasesParser(resp.data)
|
||||
|
@ -78,7 +72,7 @@ class QueriesPage extends Component {
|
|||
})
|
||||
}
|
||||
|
||||
handleKillQuery(id) {
|
||||
handleKillQuery = id => {
|
||||
const {source, killQuery} = this.props
|
||||
killQuery(source.links.proxy, id)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
NEW_DEFAULT_DATABASE,
|
||||
NEW_EMPTY_RP,
|
||||
} from 'src/admin/constants'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
const initialState = {
|
||||
users: null,
|
||||
|
|
|
@ -3,7 +3,7 @@ import React, {Component, PropTypes} from 'react'
|
|||
import _ from 'lodash'
|
||||
import classnames from 'classnames'
|
||||
import {Link} from 'react-router'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import InfiniteScroll from 'shared/components/InfiniteScroll'
|
||||
|
||||
|
@ -243,9 +243,6 @@ class SearchBar extends Component {
|
|||
this.state = {
|
||||
searchTerm: '',
|
||||
}
|
||||
|
||||
this.handleSearch = ::this.handleSearch
|
||||
this.handleChange = ::this.handleChange
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
|
@ -253,11 +250,11 @@ class SearchBar extends Component {
|
|||
this.handleSearch = _.debounce(this.handleSearch, waitPeriod)
|
||||
}
|
||||
|
||||
handleSearch() {
|
||||
handleSearch = () => {
|
||||
this.props.onSearch(this.state.searchTerm)
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
handleChange = e => {
|
||||
this.setState({searchTerm: e.target.value}, this.handleSearch)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import AJAX from 'utils/ajax'
|
|||
import _ from 'lodash'
|
||||
import moment from 'moment'
|
||||
|
||||
import timeRanges from 'hson!shared/data/timeRanges.hson'
|
||||
import {timeRanges} from 'shared/data/timeRanges'
|
||||
|
||||
class AlertsApp extends Component {
|
||||
constructor(props) {
|
||||
|
@ -35,11 +35,6 @@ class AlertsApp extends Component {
|
|||
limitMultiplier: 1, // only used if AlertsApp receives a limit prop
|
||||
isAlertsMaxedOut: false, // only used if AlertsApp receives a limit prop
|
||||
}
|
||||
|
||||
this.fetchAlerts = ::this.fetchAlerts
|
||||
this.renderSubComponents = ::this.renderSubComponents
|
||||
this.handleGetMoreAlerts = ::this.handleGetMoreAlerts
|
||||
this.handleApplyTime = ::this.handleApplyTime
|
||||
}
|
||||
|
||||
// TODO: show a loading screen until we figure out if there is a kapacitor and fetch the alerts
|
||||
|
@ -65,7 +60,7 @@ class AlertsApp extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
fetchAlerts() {
|
||||
fetchAlerts = () => {
|
||||
getAlerts(
|
||||
this.props.source.links.proxy,
|
||||
this.state.timeRange,
|
||||
|
@ -112,13 +107,13 @@ class AlertsApp extends Component {
|
|||
})
|
||||
}
|
||||
|
||||
handleGetMoreAlerts() {
|
||||
handleGetMoreAlerts = () => {
|
||||
this.setState({limitMultiplier: this.state.limitMultiplier + 1}, () => {
|
||||
this.fetchAlerts(this.state.limitMultiplier)
|
||||
})
|
||||
}
|
||||
|
||||
renderSubComponents() {
|
||||
renderSubComponents = () => {
|
||||
const {source, isWidget, limit} = this.props
|
||||
const {isAlertsMaxedOut, alerts} = this.state
|
||||
|
||||
|
@ -135,7 +130,7 @@ class AlertsApp extends Component {
|
|||
: <NoKapacitorError source={source} />
|
||||
}
|
||||
|
||||
handleApplyTime(timeRange) {
|
||||
handleApplyTime = timeRange => {
|
||||
this.setState({timeRange})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {withRouter} from 'react-router'
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
import _ from 'lodash'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import ResizeContainer from 'shared/components/ResizeContainer'
|
||||
import QueryMaker from 'src/dashboards/components/QueryMaker'
|
||||
|
|
|
@ -3,7 +3,7 @@ import {connect} from 'react-redux'
|
|||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import _ from 'lodash'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import Threshold from 'src/dashboards/components/Threshold'
|
||||
|
@ -46,7 +46,12 @@ class GaugeOptions extends Component {
|
|||
name: GAUGE_COLORS[randomColor].name,
|
||||
}
|
||||
|
||||
handleUpdateGaugeColors([...gaugeColors, newThreshold])
|
||||
const updatedColors = _.sortBy(
|
||||
[...gaugeColors, newThreshold],
|
||||
color => color.value
|
||||
)
|
||||
|
||||
handleUpdateGaugeColors(updatedColors)
|
||||
} else {
|
||||
onResetFocus()
|
||||
}
|
||||
|
@ -57,8 +62,9 @@ class GaugeOptions extends Component {
|
|||
const gaugeColors = this.props.gaugeColors.filter(
|
||||
color => color.id !== threshold.id
|
||||
)
|
||||
const sortedColors = _.sortBy(gaugeColors, color => color.value)
|
||||
|
||||
handleUpdateGaugeColors(gaugeColors)
|
||||
handleUpdateGaugeColors(sortedColors)
|
||||
onResetFocus()
|
||||
}
|
||||
|
||||
|
@ -138,12 +144,18 @@ class GaugeOptions extends Component {
|
|||
handleUpdateAxes(newAxes)
|
||||
}
|
||||
|
||||
handleSortColors = () => {
|
||||
const {gaugeColors, handleUpdateGaugeColors} = this.props
|
||||
const sortedColors = _.sortBy(gaugeColors, color => color.value)
|
||||
|
||||
handleUpdateGaugeColors(sortedColors)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {gaugeColors, axes: {y: {prefix, suffix}}} = this.props
|
||||
|
||||
const disableMaxColor = gaugeColors.length > MIN_THRESHOLDS
|
||||
const disableAddThreshold = gaugeColors.length > MAX_THRESHOLDS
|
||||
const sortedColors = _.sortBy(gaugeColors, color => color.value)
|
||||
|
||||
return (
|
||||
<FancyScrollbar
|
||||
|
@ -160,11 +172,11 @@ class GaugeOptions extends Component {
|
|||
>
|
||||
<span className="icon plus" /> Add Threshold
|
||||
</button>
|
||||
{sortedColors.map(color =>
|
||||
{gaugeColors.map(color =>
|
||||
<Threshold
|
||||
isMin={color.value === sortedColors[0].value}
|
||||
isMin={color.value === gaugeColors[0].value}
|
||||
isMax={
|
||||
color.value === sortedColors[sortedColors.length - 1].value
|
||||
color.value === gaugeColors[gaugeColors.length - 1].value
|
||||
}
|
||||
visualizationType="gauge"
|
||||
threshold={color}
|
||||
|
@ -174,6 +186,7 @@ class GaugeOptions extends Component {
|
|||
onValidateColorValue={this.handleValidateColorValue}
|
||||
onUpdateColorValue={this.handleUpdateColorValue}
|
||||
onDeleteThreshold={this.handleDeleteThreshold}
|
||||
onSortColors={this.handleSortColors}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,7 @@ import {connect} from 'react-redux'
|
|||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import _ from 'lodash'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import Threshold from 'src/dashboards/components/Threshold'
|
||||
|
@ -67,18 +67,23 @@ class SingleStatOptions extends Component {
|
|||
name: GAUGE_COLORS[randomColor].name,
|
||||
}
|
||||
|
||||
handleUpdateSingleStatColors([...singleStatColors, newThreshold])
|
||||
const updatedColors = _.sortBy(
|
||||
[...singleStatColors, newThreshold],
|
||||
color => color.value
|
||||
)
|
||||
|
||||
handleUpdateSingleStatColors(updatedColors)
|
||||
onResetFocus()
|
||||
}
|
||||
|
||||
handleDeleteThreshold = threshold => () => {
|
||||
const {handleUpdateSingleStatColors, onResetFocus} = this.props
|
||||
|
||||
const singleStatColors = this.props.singleStatColors.filter(
|
||||
color => color.id !== threshold.id
|
||||
)
|
||||
const sortedColors = _.sortBy(singleStatColors, color => color.value)
|
||||
|
||||
handleUpdateSingleStatColors(singleStatColors)
|
||||
handleUpdateSingleStatColors(sortedColors)
|
||||
onResetFocus()
|
||||
}
|
||||
|
||||
|
@ -126,6 +131,13 @@ class SingleStatOptions extends Component {
|
|||
handleUpdateAxes(newAxes)
|
||||
}
|
||||
|
||||
handleSortColors = () => {
|
||||
const {singleStatColors, handleUpdateSingleStatColors} = this.props
|
||||
const sortedColors = _.sortBy(singleStatColors, color => color.value)
|
||||
|
||||
handleUpdateSingleStatColors(sortedColors)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
singleStatColors,
|
||||
|
@ -135,8 +147,6 @@ class SingleStatOptions extends Component {
|
|||
|
||||
const disableAddThreshold = singleStatColors.length > MAX_THRESHOLDS
|
||||
|
||||
const sortedColors = _.sortBy(singleStatColors, color => color.value)
|
||||
|
||||
return (
|
||||
<FancyScrollbar
|
||||
className="display-options--cell y-axis-controls"
|
||||
|
@ -152,7 +162,7 @@ class SingleStatOptions extends Component {
|
|||
>
|
||||
<span className="icon plus" /> Add Threshold
|
||||
</button>
|
||||
{sortedColors.map(
|
||||
{singleStatColors.map(
|
||||
color =>
|
||||
color.id === SINGLE_STAT_BASE
|
||||
? <div className="gauge-controls--section" key={color.id}>
|
||||
|
@ -172,6 +182,7 @@ class SingleStatOptions extends Component {
|
|||
onValidateColorValue={this.handleValidateColorValue}
|
||||
onUpdateColorValue={this.handleUpdateColorValue}
|
||||
onDeleteThreshold={this.handleDeleteThreshold}
|
||||
onSortColors={this.handleSortColors}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -29,6 +29,13 @@ class Threshold extends Component {
|
|||
|
||||
handleBlur = () => {
|
||||
this.setState({workingValue: this.props.threshold.value, valid: true})
|
||||
this.props.onSortColors()
|
||||
}
|
||||
|
||||
handleKeyUp = e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.thresholdInputRef.blur()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -87,6 +94,8 @@ class Threshold extends Component {
|
|||
type="number"
|
||||
onChange={this.handleChangeWorkingValue}
|
||||
onBlur={this.handleBlur}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
ref={r => (this.thresholdInputRef = r)}
|
||||
/>
|
||||
<ColorDropdown
|
||||
colors={GAUGE_COLORS}
|
||||
|
@ -117,6 +126,7 @@ Threshold.propTypes = {
|
|||
onDeleteThreshold: func.isRequired,
|
||||
isMin: bool,
|
||||
isMax: bool,
|
||||
onSortColors: func.isRequired,
|
||||
}
|
||||
|
||||
export default Threshold
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import TemplateVariableTable from 'src/dashboards/components/template_variables/Table'
|
||||
|
||||
|
@ -64,15 +64,9 @@ class TemplateVariableManagerWrapper extends Component {
|
|||
rows: this.props.templates,
|
||||
isEdited: false,
|
||||
}
|
||||
|
||||
this.onRunQuerySuccess = ::this.onRunQuerySuccess
|
||||
this.onSaveTemplatesSuccess = ::this.onSaveTemplatesSuccess
|
||||
this.onAddVariable = ::this.onAddVariable
|
||||
this.onDeleteTemplateVariable = ::this.onDeleteTemplateVariable
|
||||
this.tempVarAlreadyExists = ::this.tempVarAlreadyExists
|
||||
}
|
||||
|
||||
onAddVariable() {
|
||||
onAddVariable = () => {
|
||||
const {rows} = this.state
|
||||
|
||||
const newRow = {
|
||||
|
@ -95,7 +89,7 @@ class TemplateVariableManagerWrapper extends Component {
|
|||
this.setState({rows: newRows})
|
||||
}
|
||||
|
||||
onRunQuerySuccess(template, queryConfig, parsedData, tempVar) {
|
||||
onRunQuerySuccess = (template, queryConfig, parsedData, tempVar) => {
|
||||
const {rows} = this.state
|
||||
const {id, links} = template
|
||||
const {
|
||||
|
@ -156,7 +150,7 @@ class TemplateVariableManagerWrapper extends Component {
|
|||
this.setState({rows: newRows, isEdited: true})
|
||||
}
|
||||
|
||||
onSaveTemplatesSuccess() {
|
||||
onSaveTemplatesSuccess = () => {
|
||||
const {rows} = this.state
|
||||
|
||||
const newRows = rows.map(row => ({...row, isNew: false}))
|
||||
|
@ -164,7 +158,7 @@ class TemplateVariableManagerWrapper extends Component {
|
|||
this.setState({rows: newRows, isEdited: false})
|
||||
}
|
||||
|
||||
onDeleteTemplateVariable(templateID) {
|
||||
onDeleteTemplateVariable = templateID => {
|
||||
const {rows} = this.state
|
||||
|
||||
const newRows = rows.filter(({id}) => id !== templateID)
|
||||
|
@ -172,7 +166,7 @@ class TemplateVariableManagerWrapper extends Component {
|
|||
this.setState({rows: newRows, isEdited: true})
|
||||
}
|
||||
|
||||
tempVarAlreadyExists(testTempVar, testID) {
|
||||
tempVarAlreadyExists = (testTempVar, testID) => {
|
||||
const {rows: tempVars} = this.state
|
||||
return tempVars.some(
|
||||
({tempVar, id}) => tempVar === testTempVar && id !== testID
|
||||
|
|
|
@ -119,8 +119,6 @@ class RowWrapper extends Component {
|
|||
selectedTagKey: query && query.tagKey,
|
||||
autoFocusTarget: 'tempVar',
|
||||
}
|
||||
|
||||
this.runTemplateVariableQuery = ::this.runTemplateVariableQuery
|
||||
}
|
||||
|
||||
handleSubmit = ({
|
||||
|
|
|
@ -111,7 +111,7 @@ export const DEFAULT_SINGLESTAT_COLORS = [
|
|||
hex: GAUGE_COLORS[11].hex,
|
||||
id: SINGLE_STAT_BASE,
|
||||
name: GAUGE_COLORS[11].name,
|
||||
value: 0,
|
||||
value: -999999999999999999,
|
||||
},
|
||||
]
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ import {
|
|||
templateControlBarVisibilityToggled as templateControlBarVisibilityToggledAction,
|
||||
} from 'shared/actions/app'
|
||||
import {presentationButtonDispatcher} from 'shared/dispatchers'
|
||||
import {interval, DASHBOARD_LAYOUT_ROW_HEIGHT} from 'shared/constants'
|
||||
import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'shared/constants'
|
||||
|
||||
const FORMAT_INFLUXQL = 'influxql'
|
||||
const defaultTimeRange = {
|
||||
|
@ -336,6 +336,25 @@ class DashboardPage extends Component {
|
|||
],
|
||||
}
|
||||
|
||||
const interval = {
|
||||
id: 'interval',
|
||||
type: 'autoGroupBy',
|
||||
tempVar: ':interval:',
|
||||
label: 'automatically determine the best group by time',
|
||||
values: [
|
||||
{
|
||||
value: '1000', // pixels
|
||||
type: 'resolution',
|
||||
selected: true,
|
||||
},
|
||||
{
|
||||
value: '3',
|
||||
type: 'pointsPerPixel',
|
||||
selected: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
let templatesIncludingDashTime
|
||||
if (dashboard) {
|
||||
templatesIncludingDashTime = [
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import _ from 'lodash'
|
||||
import timeRanges from 'hson!shared/data/timeRanges.hson'
|
||||
import {timeRanges} from 'shared/data/timeRanges'
|
||||
|
||||
const {lower, upper} = timeRanges.find(tr => tr.lower === 'now() - 1h')
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import {getQueryConfig} from 'shared/apis'
|
||||
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import React, {SFC} from 'react'
|
||||
import moment from 'moment'
|
||||
|
||||
const CustomCell = ({data, columnName}) => {
|
||||
export interface CustomCellProps {
|
||||
data?: string
|
||||
columnName?: string
|
||||
}
|
||||
|
||||
const CustomCell: SFC<CustomCellProps> = ({data, columnName}) => {
|
||||
if (columnName === 'time') {
|
||||
const date = moment(new Date(data)).format('MM/DD/YY hh:mm:ssA')
|
||||
|
||||
|
@ -19,11 +24,4 @@ const CustomCell = ({data, columnName}) => {
|
|||
)
|
||||
}
|
||||
|
||||
const {number, oneOfType, string} = PropTypes
|
||||
|
||||
CustomCell.propTypes = {
|
||||
data: oneOfType([number, string]),
|
||||
columnName: string.isRequired,
|
||||
}
|
||||
|
||||
export default CustomCell
|
|
@ -1,7 +1,7 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import {withRouter} from 'react-router'
|
||||
|
||||
import groupByTimeOptions from 'hson!src/data_explorer/data/groupByTimes.hson'
|
||||
import groupByTimeOptions from 'src/data_explorer/data/groupByTimes'
|
||||
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
|
||||
|
|
|
@ -10,11 +10,6 @@ class QueryEditor extends Component {
|
|||
this.state = {
|
||||
value: this.props.query,
|
||||
}
|
||||
|
||||
this.handleKeyDown = ::this.handleKeyDown
|
||||
this.handleChange = ::this.handleChange
|
||||
this.handleUpdate = ::this.handleUpdate
|
||||
this.handleChooseMetaQuery = ::this.handleChooseMetaQuery
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
|
@ -23,7 +18,7 @@ class QueryEditor extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleKeyDown(e) {
|
||||
handleKeyDown = e => {
|
||||
const {value} = this.state
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
|
@ -37,15 +32,15 @@ class QueryEditor extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleChange() {
|
||||
handleChange = () => {
|
||||
this.setState({value: this.editor.value})
|
||||
}
|
||||
|
||||
handleUpdate() {
|
||||
handleUpdate = () => {
|
||||
this.props.onUpdate(this.state.value)
|
||||
}
|
||||
|
||||
handleChooseMetaQuery(template) {
|
||||
handleChooseMetaQuery = template => {
|
||||
this.setState({value: template.query})
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import {Table, Column, Cell} from 'fixed-data-table'
|
|||
import Dropdown from 'shared/components/Dropdown'
|
||||
import CustomCell from 'src/data_explorer/components/CustomCell'
|
||||
import TabItem from 'src/data_explorer/components/TableTabItem'
|
||||
import {TEMPLATES} from 'src/shared/constants'
|
||||
import {TEMPLATES} from 'src/data_explorer/constants'
|
||||
|
||||
import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import _ from 'lodash'
|
|||
import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
||||
import {resultsToCSV} from 'src/shared/parsing/resultsToCSV.js'
|
||||
import download from 'src/external/download.js'
|
||||
import {TEMPLATES} from 'src/shared/constants'
|
||||
import {TEMPLATES} from 'src/data_explorer/constants'
|
||||
|
||||
const getCSV = (query, errorThrown) => async () => {
|
||||
try {
|
||||
|
|
|
@ -81,3 +81,16 @@ export const QUERY_TEMPLATES = [
|
|||
{text: 'Show Stats', query: 'SHOW STATS'},
|
||||
{text: 'Show Diagnostics', query: 'SHOW DIAGNOSTICS'},
|
||||
]
|
||||
|
||||
const interval = {
|
||||
id: 'interval',
|
||||
type: 'autoGroupBy',
|
||||
tempVar: ':interval:',
|
||||
label: 'automatically determine the best group by time',
|
||||
values: [
|
||||
{value: '1000', type: 'resolution', selected: true},
|
||||
{value: '3', type: 'pointsPerPixel', selected: true},
|
||||
],
|
||||
} // pixels
|
||||
|
||||
export const TEMPLATES = [interval]
|
||||
|
|
|
@ -6,16 +6,16 @@ import queryString from 'query-string'
|
|||
|
||||
import _ from 'lodash'
|
||||
|
||||
import QueryMaker from 'src/data_explorer/components/QueryMaker'
|
||||
import Visualization from 'src/data_explorer/components/Visualization'
|
||||
import QueryMaker from '../components/QueryMaker'
|
||||
import Visualization from '../components/Visualization'
|
||||
import WriteDataForm from 'src/data_explorer/components/WriteDataForm'
|
||||
import Header from 'src/data_explorer/containers/Header'
|
||||
import ResizeContainer from 'src/shared/components/ResizeContainer'
|
||||
import OverlayTechnologies from 'src/shared/components/OverlayTechnologies'
|
||||
import Header from '../containers/Header'
|
||||
import ResizeContainer from 'shared/components/ResizeContainer'
|
||||
import OverlayTechnologies from 'shared/components/OverlayTechnologies'
|
||||
import ManualRefresh from 'src/shared/components/ManualRefresh'
|
||||
|
||||
import {VIS_VIEWS, AUTO_GROUP_BY, TEMPLATES} from 'src/shared/constants'
|
||||
import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants'
|
||||
import {VIS_VIEWS, AUTO_GROUP_BY} from 'shared/constants'
|
||||
import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS, TEMPLATES} from '../constants'
|
||||
import {errorThrown} from 'shared/actions/errors'
|
||||
import {setAutoRefresh} from 'shared/actions/app'
|
||||
import * as dataExplorerActionCreators from 'src/data_explorer/actions/view'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[
|
||||
const groupByTimes = [
|
||||
{defaultTimeBound: ':interval:', seconds: 604800, menuOption: 'auto'},
|
||||
{defaultTimeBound: 'now() - 5m', seconds: 10, menuOption: '10s'},
|
||||
{defaultTimeBound: 'now() - 15m', seconds: 60, menuOption: '1m'},
|
||||
|
@ -10,3 +10,5 @@
|
|||
{defaultTimeBound: 'now() - 7d', seconds: 86400, menuOption: '1d'},
|
||||
{defaultTimeBound: 'now() - 30d', seconds: 604800, menuOption: '7d'},
|
||||
]
|
||||
|
||||
export default groupByTimes
|
|
@ -1,4 +1,4 @@
|
|||
import timeRanges from 'hson!shared/data/timeRanges.hson'
|
||||
import {timeRanges} from 'shared/data/timeRanges'
|
||||
|
||||
const {lower, upper} = timeRanges.find(tr => tr.lower === 'now() - 1h')
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import DashboardHeader from 'src/dashboards/components/DashboardHeader'
|
|||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import ManualRefresh from 'src/shared/components/ManualRefresh'
|
||||
|
||||
import timeRanges from 'hson!shared/data/timeRanges.hson'
|
||||
import {timeRanges} from 'shared/data/timeRanges'
|
||||
import {
|
||||
getLayouts,
|
||||
getAppsForHosts,
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
|
||||
<title>Chronograf</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id='react-root' data-basepath=""></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
<title>Chronograf</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id='react-root' data-basepath=""></div>
|
||||
</body>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,4 +1,4 @@
|
|||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
import {getActiveKapacitor} from 'shared/apis'
|
||||
import {publishNotification} from 'shared/actions/notifications'
|
||||
import {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
const HandlerTabs = ({
|
||||
handlersOnThisAlert,
|
||||
|
|
|
@ -5,8 +5,15 @@ import FancyScrollbar from 'shared/components/FancyScrollbar'
|
|||
|
||||
class KapacitorForm extends Component {
|
||||
render() {
|
||||
const {onInputChange, onReset, kapacitor, onSubmit, exists} = this.props
|
||||
const {url: kapaUrl, name, username, password} = kapacitor
|
||||
const {
|
||||
onInputChange,
|
||||
onChangeUrl,
|
||||
onReset,
|
||||
kapacitor,
|
||||
onSubmit,
|
||||
exists,
|
||||
} = this.props
|
||||
const {url, name, username, password} = kapacitor
|
||||
return (
|
||||
<div className="page">
|
||||
<div className="page-header">
|
||||
|
@ -35,9 +42,9 @@ class KapacitorForm extends Component {
|
|||
className="form-control"
|
||||
id="kapaUrl"
|
||||
name="kapaUrl"
|
||||
placeholder={kapaUrl}
|
||||
value={kapaUrl}
|
||||
onChange={onInputChange}
|
||||
placeholder={url}
|
||||
value={url}
|
||||
onChange={onChangeUrl}
|
||||
spellCheck="false"
|
||||
/>
|
||||
</div>
|
||||
|
@ -145,6 +152,7 @@ const {func, shape, string, bool} = PropTypes
|
|||
KapacitorForm.propTypes = {
|
||||
onSubmit: func.isRequired,
|
||||
onInputChange: func.isRequired,
|
||||
onChangeUrl: func.isRequired,
|
||||
onReset: func.isRequired,
|
||||
kapacitor: shape({
|
||||
url: string.isRequired,
|
||||
|
|
|
@ -9,7 +9,7 @@ import FancyScrollbar from 'shared/components/FancyScrollbar'
|
|||
|
||||
import {createRule, editRule} from 'src/kapacitor/apis'
|
||||
import buildInfluxQLQuery from 'utils/influxql'
|
||||
import timeRanges from 'hson!shared/data/timeRanges.hson'
|
||||
import {timeRanges} from 'shared/data/timeRanges'
|
||||
import {DEFAULT_RULE_ID} from 'src/kapacitor/constants'
|
||||
|
||||
class KapacitorRule extends Component {
|
||||
|
|
|
@ -60,6 +60,10 @@ class KapacitorPage extends Component {
|
|||
})
|
||||
}
|
||||
|
||||
handleChangeUrl = ({value}) => {
|
||||
this.setState({kapacitor: {...this.state.kapacitor, url: value}})
|
||||
}
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault()
|
||||
const {
|
||||
|
@ -143,6 +147,7 @@ class KapacitorPage extends Component {
|
|||
<KapacitorForm
|
||||
onSubmit={this.handleSubmit}
|
||||
onInputChange={this.handleInputChange}
|
||||
onChangeUrl={this.handleChangeUrl}
|
||||
onReset={this.handleResetToDefaults}
|
||||
kapacitor={kapacitor}
|
||||
source={source}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import Tickscript from 'src/kapacitor/components/Tickscript'
|
||||
import * as kapactiorActionCreators from 'src/kapacitor/actions/view'
|
||||
|
|
|
@ -222,14 +222,14 @@ AnnotationSpan.propTypes = {
|
|||
annotation: schema.annotation.isRequired,
|
||||
mode: string.isRequired,
|
||||
dygraph: shape({}).isRequired,
|
||||
staticLegendHeight: number.isRequired,
|
||||
staticLegendHeight: number,
|
||||
updateAnnotationAsync: func.isRequired,
|
||||
updateAnnotation: func.isRequired,
|
||||
}
|
||||
|
||||
const mdtp = {
|
||||
const mapDispatchToProps = {
|
||||
updateAnnotationAsync: actions.updateAnnotationAsync,
|
||||
updateAnnotation: actions.updateAnnotation,
|
||||
}
|
||||
|
||||
export default connect(null, mdtp)(AnnotationSpan)
|
||||
export default connect(null, mapDispatchToProps)(AnnotationSpan)
|
||||
|
|
|
@ -3,7 +3,6 @@ import _ from 'lodash'
|
|||
|
||||
import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
||||
import {removeUnselectedTemplateValues} from 'src/dashboards/constants'
|
||||
import {intervalValuesPoints} from 'src/shared/constants'
|
||||
|
||||
const AutoRefresh = ComposedComponent => {
|
||||
class wrapper extends Component {
|
||||
|
@ -97,38 +96,31 @@ const AutoRefresh = ComposedComponent => {
|
|||
const timeSeriesPromises = queries.map(query => {
|
||||
const {host, database, rp} = query
|
||||
|
||||
const templatesWithIntervalVals = templates.map(temp => {
|
||||
const templatesWithResolution = templates.map(temp => {
|
||||
if (temp.tempVar === ':interval:') {
|
||||
if (resolution) {
|
||||
// resize event
|
||||
return {
|
||||
...temp,
|
||||
values: temp.values.map(v => {
|
||||
if (v.type === 'resolution') {
|
||||
return {...v, value: `${resolution}`}
|
||||
}
|
||||
if (v.type === 'points') {
|
||||
return {
|
||||
...v,
|
||||
value: `${_.toInteger(Number(resolution) / 3)}`,
|
||||
}
|
||||
}
|
||||
return v
|
||||
}),
|
||||
values: temp.values.map(
|
||||
v => (temp.type === 'resolution' ? {...v, resolution} : v)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...temp,
|
||||
values: intervalValuesPoints,
|
||||
values: [
|
||||
...temp.values,
|
||||
{value: '1000', type: 'resolution', selected: true},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
return temp
|
||||
})
|
||||
|
||||
const tempVars = removeUnselectedTemplateValues(
|
||||
templatesWithIntervalVals
|
||||
)
|
||||
const tempVars = removeUnselectedTemplateValues(templatesWithResolution)
|
||||
|
||||
return fetchTimeSeriesAsync(
|
||||
{
|
||||
source: host,
|
||||
|
@ -206,6 +198,10 @@ const AutoRefresh = ComposedComponent => {
|
|||
: false
|
||||
}
|
||||
|
||||
wrapper.defaultProps = {
|
||||
inView: true,
|
||||
}
|
||||
|
||||
const {
|
||||
array,
|
||||
arrayOf,
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, {PropTypes, Component} from 'react'
|
|||
import classnames from 'classnames'
|
||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||
|
||||
import autoRefreshItems from 'hson!shared/data/autoRefreshes.hson'
|
||||
import autoRefreshItems from 'shared/data/autoRefreshes'
|
||||
|
||||
class AutoRefreshDropdown extends Component {
|
||||
constructor(props) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, {PropTypes, Component} from 'react'
|
|||
import rome from 'rome'
|
||||
import moment from 'moment'
|
||||
|
||||
import shortcuts from 'hson!shared/data/timeRangeShortcuts.hson'
|
||||
import shortcuts from 'shared/data/timeRangeShortcuts'
|
||||
const dateFormat = 'YYYY-MM-DD HH:mm'
|
||||
|
||||
class CustomTimeRange extends Component {
|
||||
|
|
|
@ -12,20 +12,17 @@ class CustomTimeRangeDropdown extends Component {
|
|||
this.state = {
|
||||
expanded: false,
|
||||
}
|
||||
|
||||
this.handleToggleDropdown = ::this.handleToggleDropdown
|
||||
this.handleCloseDropdown = ::this.handleCloseDropdown
|
||||
}
|
||||
|
||||
handleClickOutside() {
|
||||
this.handleCloseDropdown()
|
||||
}
|
||||
|
||||
handleToggleDropdown() {
|
||||
handleToggleDropdown = () => {
|
||||
this.setState({expanded: !this.state.expanded})
|
||||
}
|
||||
|
||||
handleCloseDropdown() {
|
||||
handleCloseDropdown = () => {
|
||||
this.setState({expanded: false})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
|
||||
import {showDatabases} from 'shared/apis/metaQuery'
|
||||
|
|
|
@ -30,15 +30,13 @@ class DeleteConfirmButtons extends Component {
|
|||
this.state = {
|
||||
isConfirming: false,
|
||||
}
|
||||
this.handleClickDelete = ::this.handleClickDelete
|
||||
this.handleCancel = ::this.handleCancel
|
||||
}
|
||||
|
||||
handleClickDelete() {
|
||||
handleClickDelete = () => {
|
||||
this.setState({isConfirming: true})
|
||||
}
|
||||
|
||||
handleCancel() {
|
||||
handleCancel = () => {
|
||||
this.setState({isConfirming: false})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import {Link} from 'react-router'
|
||||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index'
|
||||
import DropdownMenu, {DropdownMenuEmpty} from 'shared/components/DropdownMenu'
|
||||
import DropdownInput from 'shared/components/DropdownInput'
|
||||
import DropdownHead from 'shared/components/DropdownHead'
|
||||
|
||||
class Dropdown extends Component {
|
||||
export class Dropdown extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
|
@ -36,6 +37,7 @@ class Dropdown extends Component {
|
|||
if (disabled) {
|
||||
return
|
||||
}
|
||||
|
||||
this.toggleMenu(e)
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick(e)
|
||||
|
@ -56,6 +58,7 @@ class Dropdown extends Component {
|
|||
if (e) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
if (!this.state.isOpen) {
|
||||
this.setState({
|
||||
searchTerm: '',
|
||||
|
@ -63,6 +66,7 @@ class Dropdown extends Component {
|
|||
highlightedItemIndex: null,
|
||||
})
|
||||
}
|
||||
|
||||
this.setState({isOpen: !this.state.isOpen})
|
||||
}
|
||||
|
||||
|
@ -95,17 +99,17 @@ class Dropdown extends Component {
|
|||
}
|
||||
|
||||
handleFilterChange = e => {
|
||||
if (e.target.value === null || e.target.value === '') {
|
||||
this.setState({
|
||||
searchTerm: '',
|
||||
filteredItems: this.props.items,
|
||||
highlightedItemIndex: null,
|
||||
})
|
||||
} else {
|
||||
this.setState({searchTerm: e.target.value}, () =>
|
||||
if (e.target.value) {
|
||||
return this.setState({searchTerm: e.target.value}, () =>
|
||||
this.applyFilter(this.state.searchTerm)
|
||||
)
|
||||
}
|
||||
|
||||
this.setState({
|
||||
searchTerm: '',
|
||||
filteredItems: this.props.items,
|
||||
highlightedItemIndex: null,
|
||||
})
|
||||
}
|
||||
|
||||
applyFilter = searchTerm => {
|
||||
|
@ -121,108 +125,29 @@ class Dropdown extends Component {
|
|||
})
|
||||
}
|
||||
|
||||
renderMenu() {
|
||||
const {
|
||||
actions,
|
||||
addNew,
|
||||
items,
|
||||
menuWidth,
|
||||
menuLabel,
|
||||
menuClass,
|
||||
useAutoComplete,
|
||||
selected,
|
||||
} = this.props
|
||||
const {filteredItems, highlightedItemIndex} = this.state
|
||||
const menuItems = useAutoComplete ? filteredItems : items
|
||||
|
||||
return (
|
||||
<ul
|
||||
className={classnames('dropdown-menu', {
|
||||
'dropdown-menu--no-highlight': useAutoComplete,
|
||||
[menuClass]: menuClass,
|
||||
})}
|
||||
style={{width: menuWidth}}
|
||||
>
|
||||
<FancyScrollbar
|
||||
autoHide={false}
|
||||
autoHeight={true}
|
||||
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
||||
>
|
||||
{menuLabel
|
||||
? <li className="dropdown-header">
|
||||
{menuLabel}
|
||||
</li>
|
||||
: null}
|
||||
{menuItems.map((item, i) => {
|
||||
if (item.text === 'SEPARATOR') {
|
||||
return <li key={i} className="dropdown-divider" />
|
||||
}
|
||||
return (
|
||||
<li
|
||||
className={classnames('dropdown-item', {
|
||||
highlight: i === highlightedItemIndex,
|
||||
active: item.text === selected,
|
||||
})}
|
||||
key={i}
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
onClick={this.handleSelection(item)}
|
||||
onMouseOver={this.handleHighlight(i)}
|
||||
>
|
||||
{item.text}
|
||||
</a>
|
||||
{actions && actions.length
|
||||
? <div className="dropdown-actions">
|
||||
{actions.map(action => {
|
||||
return (
|
||||
<button
|
||||
key={action.text}
|
||||
className="dropdown-action"
|
||||
onClick={this.handleAction(action, item)}
|
||||
>
|
||||
<span
|
||||
title={action.text}
|
||||
className={`icon ${action.icon}`}
|
||||
/>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
: null}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
{addNew
|
||||
? <li className="multi-select--apply">
|
||||
<Link className="btn btn-xs btn-default" to={addNew.url}>
|
||||
{addNew.text}
|
||||
</Link>
|
||||
</li>
|
||||
: null}
|
||||
</FancyScrollbar>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
items,
|
||||
addNew,
|
||||
actions,
|
||||
selected,
|
||||
disabled,
|
||||
iconName,
|
||||
tabIndex,
|
||||
className,
|
||||
menuClass,
|
||||
iconName,
|
||||
menuWidth,
|
||||
menuLabel,
|
||||
buttonSize,
|
||||
buttonColor,
|
||||
toggleStyle,
|
||||
useAutoComplete,
|
||||
disabled,
|
||||
tabIndex,
|
||||
} = this.props
|
||||
const {isOpen, searchTerm, filteredItems} = this.state
|
||||
const menuItems = useAutoComplete ? filteredItems : items
|
||||
|
||||
const {isOpen, searchTerm, filteredItems, highlightedItemIndex} = this.state
|
||||
const menuItems = useAutoComplete ? filteredItems : items
|
||||
const disabledClass = disabled ? 'disabled' : null
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={this.handleClick}
|
||||
|
@ -232,48 +157,46 @@ class Dropdown extends Component {
|
|||
})}
|
||||
tabIndex={tabIndex}
|
||||
ref={r => (this.dropdownRef = r)}
|
||||
data-test="dropdown-toggle"
|
||||
>
|
||||
{useAutoComplete && isOpen
|
||||
? <div
|
||||
className={`dropdown-autocomplete dropdown-toggle ${buttonSize} ${buttonColor} ${disabledClass}`}
|
||||
style={toggleStyle}
|
||||
>
|
||||
<input
|
||||
ref="dropdownAutoComplete"
|
||||
className="dropdown-autocomplete--input"
|
||||
type="text"
|
||||
autoFocus={true}
|
||||
placeholder="Filter items..."
|
||||
spellCheck={false}
|
||||
onChange={this.handleFilterChange}
|
||||
onKeyDown={this.handleFilterKeyPress}
|
||||
value={searchTerm}
|
||||
/>
|
||||
<span className="caret" />
|
||||
</div>
|
||||
: <div
|
||||
className={`btn dropdown-toggle ${buttonSize} ${buttonColor} ${disabledClass}`}
|
||||
style={toggleStyle}
|
||||
>
|
||||
{iconName
|
||||
? <span className={classnames('icon', {[iconName]: true})} />
|
||||
: null}
|
||||
<span className="dropdown-selected">
|
||||
{selected}
|
||||
</span>
|
||||
<span className="caret" />
|
||||
</div>}
|
||||
{isOpen && menuItems.length ? this.renderMenu() : null}
|
||||
{isOpen && !menuItems.length
|
||||
? <ul
|
||||
className={classnames('dropdown-menu', {
|
||||
'dropdown-menu--no-highlight': useAutoComplete,
|
||||
[menuClass]: menuClass,
|
||||
})}
|
||||
>
|
||||
<li className="dropdown-empty">No matching items</li>
|
||||
</ul>
|
||||
: null}
|
||||
? <DropdownInput
|
||||
searchTerm={searchTerm}
|
||||
buttonSize={buttonSize}
|
||||
buttonColor={buttonColor}
|
||||
toggleStyle={toggleStyle}
|
||||
disabledClass={disabledClass}
|
||||
onFilterChange={this.handleFilterChange}
|
||||
onFilterKeyPress={this.handleFilterKeyPress}
|
||||
/>
|
||||
: <DropdownHead
|
||||
iconName={iconName}
|
||||
selected={selected}
|
||||
searchTerm={searchTerm}
|
||||
buttonSize={buttonSize}
|
||||
buttonColor={buttonColor}
|
||||
toggleStyle={toggleStyle}
|
||||
disabledClass={disabledClass}
|
||||
/>}
|
||||
{isOpen && menuItems.length
|
||||
? <DropdownMenu
|
||||
addNew={addNew}
|
||||
actions={actions}
|
||||
items={menuItems}
|
||||
selected={selected}
|
||||
menuClass={menuClass}
|
||||
menuWidth={menuWidth}
|
||||
menuLabel={menuLabel}
|
||||
onAction={this.handleAction}
|
||||
useAutoComplete={useAutoComplete}
|
||||
onSelection={this.handleSelection}
|
||||
onHighlight={this.handleHighlight}
|
||||
highlightedItemIndex={highlightedItemIndex}
|
||||
/>
|
||||
: <DropdownMenuEmpty
|
||||
useAutoComplete={useAutoComplete}
|
||||
menuClass={menuClass}
|
||||
/>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const DropdownHead = ({
|
||||
iconName,
|
||||
selected,
|
||||
buttonSize,
|
||||
toggleStyle,
|
||||
buttonColor,
|
||||
disabledClass,
|
||||
}) =>
|
||||
<div
|
||||
className={`btn dropdown-toggle ${buttonSize} ${buttonColor} ${disabledClass}`}
|
||||
style={toggleStyle}
|
||||
>
|
||||
{iconName && <span className={classnames('icon', {[iconName]: true})} />}
|
||||
<span className="dropdown-selected">
|
||||
{selected}
|
||||
</span>
|
||||
<span className="caret" />
|
||||
</div>
|
||||
|
||||
const {string, shape} = PropTypes
|
||||
|
||||
DropdownHead.propTypes = {
|
||||
iconName: string,
|
||||
selected: string,
|
||||
buttonSize: string,
|
||||
toggleStyle: shape(),
|
||||
buttonColor: string,
|
||||
disabledClass: string,
|
||||
}
|
||||
|
||||
export default DropdownHead
|
|
@ -0,0 +1,42 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
const DropdownInput = ({
|
||||
searchTerm,
|
||||
buttonSize,
|
||||
buttonColor,
|
||||
toggleStyle,
|
||||
disabledClass,
|
||||
onFilterChange,
|
||||
onFilterKeyPress,
|
||||
}) =>
|
||||
<div
|
||||
className={`dropdown-autocomplete dropdown-toggle ${buttonSize} ${buttonColor} ${disabledClass}`}
|
||||
style={toggleStyle}
|
||||
>
|
||||
<input
|
||||
className="dropdown-autocomplete--input"
|
||||
type="text"
|
||||
autoFocus={true}
|
||||
placeholder="Filter items..."
|
||||
spellCheck={false}
|
||||
onChange={onFilterChange}
|
||||
onKeyDown={onFilterKeyPress}
|
||||
value={searchTerm}
|
||||
/>
|
||||
<span className="caret" />
|
||||
</div>
|
||||
|
||||
export default DropdownInput
|
||||
|
||||
const {func, shape, string} = PropTypes
|
||||
|
||||
DropdownInput.propTypes = {
|
||||
searchTerm: string,
|
||||
buttonSize: string,
|
||||
buttonColor: string,
|
||||
toggleStyle: shape({}),
|
||||
disabledClass: string,
|
||||
onFilterChange: func,
|
||||
onFilterKeyPress: func,
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {Link} from 'react-router'
|
||||
|
||||
import classnames from 'classnames'
|
||||
import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
|
||||
// AddNewResource is an optional parameter that takes the user to another
|
||||
// route defined by url prop
|
||||
const AddNewButton = ({url, text}) =>
|
||||
<li className="multi-select--apply">
|
||||
<Link className="btn btn-xs btn-default" to={url}>
|
||||
{text}
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
const DropdownMenu = ({
|
||||
items,
|
||||
addNew,
|
||||
actions,
|
||||
selected,
|
||||
onAction,
|
||||
menuClass,
|
||||
menuWidth,
|
||||
menuLabel,
|
||||
onSelection,
|
||||
onHighlight,
|
||||
useAutoComplete,
|
||||
highlightedItemIndex,
|
||||
}) => {
|
||||
return (
|
||||
<ul
|
||||
className={classnames('dropdown-menu', {
|
||||
'dropdown-menu--no-highlight': useAutoComplete,
|
||||
[menuClass]: menuClass,
|
||||
})}
|
||||
style={{width: menuWidth}}
|
||||
data-test="dropdown-ul"
|
||||
>
|
||||
<FancyScrollbar
|
||||
autoHide={false}
|
||||
autoHeight={true}
|
||||
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
||||
>
|
||||
{menuLabel
|
||||
? <li className="dropdown-header">
|
||||
{menuLabel}
|
||||
</li>
|
||||
: null}
|
||||
{items.map((item, i) => {
|
||||
if (item.text === 'SEPARATOR') {
|
||||
return <li key={i} className="dropdown-divider" />
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
className={classnames('dropdown-item', {
|
||||
highlight: i === highlightedItemIndex,
|
||||
active: item.text === selected,
|
||||
})}
|
||||
data-test="dropdown-item"
|
||||
key={i}
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
onClick={onSelection(item)}
|
||||
onMouseOver={onHighlight(i)}
|
||||
>
|
||||
{item.text}
|
||||
</a>
|
||||
{actions && actions.length
|
||||
? <div className="dropdown-actions">
|
||||
{actions.map(action => {
|
||||
return (
|
||||
<button
|
||||
key={action.text}
|
||||
className="dropdown-action"
|
||||
onClick={onAction(action, item)}
|
||||
>
|
||||
<span
|
||||
title={action.text}
|
||||
className={`icon ${action.icon}`}
|
||||
/>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
: null}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
{addNew && <AddNewButton url={addNew.url} text={addNew.text} />}
|
||||
</FancyScrollbar>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
export const DropdownMenuEmpty = ({useAutoComplete, menuClass}) =>
|
||||
<ul
|
||||
className={classnames('dropdown-menu', {
|
||||
'dropdown-menu--no-highlight': useAutoComplete,
|
||||
[menuClass]: menuClass,
|
||||
})}
|
||||
>
|
||||
<li className="dropdown-empty">No matching items</li>
|
||||
</ul>
|
||||
|
||||
const {arrayOf, bool, number, shape, string, func} = PropTypes
|
||||
|
||||
AddNewButton.propTypes = {
|
||||
url: string,
|
||||
text: string,
|
||||
}
|
||||
|
||||
DropdownMenuEmpty.propTypes = {
|
||||
useAutoComplete: bool,
|
||||
menuClass: string,
|
||||
}
|
||||
|
||||
DropdownMenu.propTypes = {
|
||||
onAction: func,
|
||||
actions: arrayOf(
|
||||
shape({
|
||||
icon: string.isRequired,
|
||||
text: string.isRequired,
|
||||
handler: func.isRequired,
|
||||
})
|
||||
),
|
||||
items: arrayOf(
|
||||
shape({
|
||||
text: string.isRequired,
|
||||
})
|
||||
).isRequired,
|
||||
onClick: func,
|
||||
addNew: shape({
|
||||
url: string.isRequired,
|
||||
text: string.isRequired,
|
||||
}),
|
||||
selected: string.isRequired,
|
||||
iconName: string,
|
||||
className: string,
|
||||
buttonColor: string,
|
||||
menuWidth: string,
|
||||
menuLabel: string,
|
||||
menuClass: string,
|
||||
useAutoComplete: bool,
|
||||
disabled: bool,
|
||||
searchTerm: string,
|
||||
onSelection: func,
|
||||
onHighlight: func,
|
||||
highlightedItemIndex: number,
|
||||
}
|
||||
|
||||
export default DropdownMenu
|
|
@ -301,8 +301,9 @@ class Dygraph extends Component {
|
|||
|
||||
render() {
|
||||
const {isHidden, staticLegendHeight} = this.state
|
||||
const {staticLegend} = this.props
|
||||
const {staticLegend, children} = this.props
|
||||
|
||||
const nestedGraph = (children && children.length && children[0]) || children
|
||||
let dygraphStyle = {...this.props.containerStyle, zIndex: '2'}
|
||||
if (staticLegend) {
|
||||
const cellVerticalPadding = 16
|
||||
|
@ -339,17 +340,19 @@ class Dygraph extends Component {
|
|||
/>
|
||||
{staticLegend &&
|
||||
<StaticLegend
|
||||
dygraphSeries={this.hashColorDygraphSeries()}
|
||||
dygraph={this.dygraph}
|
||||
handleReceiveStaticLegendHeight={
|
||||
this.handleReceiveStaticLegendHeight
|
||||
}
|
||||
/>}
|
||||
{nestedGraph && React.cloneElement(nestedGraph, {staticLegendHeight})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {array, arrayOf, bool, func, shape, string} = PropTypes
|
||||
const {array, arrayOf, bool, func, node, shape, string} = PropTypes
|
||||
|
||||
Dygraph.defaultProps = {
|
||||
axes: {
|
||||
|
@ -408,6 +411,7 @@ Dygraph.propTypes = {
|
|||
dygraphRef: func,
|
||||
onZoom: func,
|
||||
mode: string,
|
||||
children: node,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({annotations: {mode}}) => ({
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import _ from 'lodash'
|
||||
import classnames from 'classnames'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import {makeLegendStyles, removeMeasurement} from 'shared/graphs/helpers'
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import {Scrollbars} from 'react-custom-scrollbars'
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import Dropdown from 'shared/components/Dropdown'
|
|||
|
||||
import {NULL_STRING, NUMBER} from 'shared/constants/queryFillOptions'
|
||||
|
||||
import queryFills from 'hson!shared/data/queryFills.hson'
|
||||
import queryFills from 'shared/data/queryFills'
|
||||
|
||||
class FillQuery extends Component {
|
||||
constructor(props) {
|
||||
|
|
|
@ -10,10 +10,6 @@ class FunctionSelector extends Component {
|
|||
this.state = {
|
||||
localSelectedItems: this.props.selectedItems,
|
||||
}
|
||||
|
||||
this.onSelect = ::this.onSelect
|
||||
this.onSingleSelect = ::this.onSingleSelect
|
||||
this.handleApplyFunctions = ::this.handleApplyFunctions
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
|
@ -22,7 +18,7 @@ class FunctionSelector extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
onSelect(item, e) {
|
||||
onSelect = (item, e) => {
|
||||
e.stopPropagation()
|
||||
|
||||
const {localSelectedItems} = this.state
|
||||
|
@ -37,7 +33,7 @@ class FunctionSelector extends Component {
|
|||
this.setState({localSelectedItems: nextItems})
|
||||
}
|
||||
|
||||
onSingleSelect(item) {
|
||||
onSingleSelect = item => {
|
||||
if (item === this.state.localSelectedItems[0]) {
|
||||
this.props.onApply([])
|
||||
this.setState({localSelectedItems: []})
|
||||
|
@ -47,11 +43,11 @@ class FunctionSelector extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
isSelected(item) {
|
||||
isSelected = item => {
|
||||
return !!this.state.localSelectedItems.find(text => text === item)
|
||||
}
|
||||
|
||||
handleApplyFunctions(e) {
|
||||
handleApplyFunctions = e => {
|
||||
e.stopPropagation()
|
||||
|
||||
this.props.onApply(this.state.localSelectedItems)
|
||||
|
|
|
@ -93,13 +93,8 @@ class LineGraph extends Component {
|
|||
top: '8px',
|
||||
}
|
||||
|
||||
let prefix
|
||||
let suffix
|
||||
|
||||
if (axes) {
|
||||
prefix = axes.y.prefix
|
||||
suffix = axes.y.suffix
|
||||
}
|
||||
const prefix = axes ? axes.y.prefix : ''
|
||||
const suffix = axes ? axes.y.suffix : ''
|
||||
|
||||
return (
|
||||
<div className="dygraph graph--hasYLabel" style={{height: '100%'}}>
|
||||
|
@ -123,17 +118,17 @@ class LineGraph extends Component {
|
|||
containerStyle={containerStyle}
|
||||
staticLegend={staticLegend}
|
||||
isGraphFilled={showSingleStat ? false : isGraphFilled}
|
||||
/>
|
||||
{showSingleStat
|
||||
? <SingleStat
|
||||
>
|
||||
{showSingleStat &&
|
||||
<SingleStat
|
||||
prefix={prefix}
|
||||
suffix={suffix}
|
||||
data={data}
|
||||
lineGraph={true}
|
||||
colors={colors}
|
||||
cellHeight={cellHeight}
|
||||
/>
|
||||
: null}
|
||||
/>}
|
||||
</Dygraph>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import {connect} from 'react-redux'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||
import AnnotationWindow from 'shared/components/AnnotationWindow'
|
||||
|
|
|
@ -15,9 +15,6 @@ import {
|
|||
class Notifications extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.renderNotification = ::this.renderNotification
|
||||
this.renderDismiss = ::this.renderDismiss
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
|
@ -26,7 +23,7 @@ class Notifications extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
renderNotification(type, message) {
|
||||
renderNotification = (type, message) => {
|
||||
const isDismissed = this.props.dismissedNotifications[
|
||||
getNotificationID(message, type)
|
||||
]
|
||||
|
@ -48,7 +45,7 @@ class Notifications extends Component {
|
|||
|
||||
handleDismiss = type => () => this.props.dismissNotification(type)
|
||||
|
||||
renderDismiss(type) {
|
||||
renderDismiss = type => {
|
||||
return (
|
||||
<button
|
||||
className="close"
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
import React from 'react'
|
||||
import React, {Component} from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
export default function enhanceWithClickOutside(WrappedComponent) {
|
||||
const componentName = WrappedComponent.displayName || WrappedComponent.name
|
||||
|
||||
return React.createClass({
|
||||
displayName: `Wrapped${componentName}`,
|
||||
|
||||
return class extends Component {
|
||||
componentDidMount() {
|
||||
document.addEventListener('click', this.handleClickOutside, true)
|
||||
},
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('click', this.handleClickOutside, true)
|
||||
},
|
||||
}
|
||||
|
||||
handleClickOutside(e) {
|
||||
handleClickOutside = e => {
|
||||
const domNode = ReactDOM.findDOMNode(this)
|
||||
if (
|
||||
(!domNode || !domNode.contains(e.target)) &&
|
||||
|
@ -23,7 +19,7 @@ export default function enhanceWithClickOutside(WrappedComponent) {
|
|||
) {
|
||||
this.wrappedComponent.handleClickOutside(e)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
@ -32,6 +28,6 @@ export default function enhanceWithClickOutside(WrappedComponent) {
|
|||
ref={ref => (this.wrappedComponent = ref)}
|
||||
/>
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import ClickOutsideInput from 'shared/components/ClickOutsideInput'
|
||||
|
||||
|
|
|
@ -17,11 +17,6 @@ class ResizeContainer extends Component {
|
|||
topHeight: props.initialTopHeight,
|
||||
bottomHeight: props.initialBottomHeight,
|
||||
}
|
||||
|
||||
this.handleStartDrag = ::this.handleStartDrag
|
||||
this.handleStopDrag = ::this.handleStopDrag
|
||||
this.handleMouseLeave = ::this.handleMouseLeave
|
||||
this.handleDrag = ::this.handleDrag
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -38,19 +33,19 @@ class ResizeContainer extends Component {
|
|||
})
|
||||
}
|
||||
|
||||
handleStartDrag() {
|
||||
handleStartDrag = () => {
|
||||
this.setState({isDragging: true})
|
||||
}
|
||||
|
||||
handleStopDrag() {
|
||||
handleStopDrag = () => {
|
||||
this.setState({isDragging: false})
|
||||
}
|
||||
|
||||
handleMouseLeave() {
|
||||
handleMouseLeave = () => {
|
||||
this.setState({isDragging: false})
|
||||
}
|
||||
|
||||
handleDrag(e) {
|
||||
handleDrag = e => {
|
||||
if (!this.state.isDragging) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import ReactTooltip from 'react-tooltip'
|
||||
|
|
|
@ -3,6 +3,7 @@ import classnames from 'classnames'
|
|||
import lastValues from 'shared/parsing/lastValues'
|
||||
|
||||
import {SMALL_CELL_HEIGHT} from 'shared/graphs/helpers'
|
||||
import {DYGRAPH_CONTAINER_V_MARGIN} from 'shared/constants'
|
||||
import {SINGLE_STAT_TEXT} from 'src/dashboards/constants/gaugeColors'
|
||||
import {generateSingleStatHexs} from 'shared/constants/colorOperations'
|
||||
|
||||
|
@ -16,6 +17,7 @@ class SingleStat extends PureComponent {
|
|||
prefix,
|
||||
suffix,
|
||||
lineGraph,
|
||||
staticLegendHeight,
|
||||
} = this.props
|
||||
|
||||
// If data for this graph is being fetched for the first time, show a graph-wide spinner.
|
||||
|
@ -39,11 +41,24 @@ class SingleStat extends PureComponent {
|
|||
lastValue
|
||||
)
|
||||
|
||||
const backgroundColor = bgColor
|
||||
const color = textColor
|
||||
const height = `calc(100% - ${staticLegendHeight +
|
||||
DYGRAPH_CONTAINER_V_MARGIN * 2}px)`
|
||||
|
||||
const singleStatStyles = staticLegendHeight
|
||||
? {
|
||||
backgroundColor,
|
||||
color,
|
||||
height,
|
||||
}
|
||||
: {
|
||||
backgroundColor,
|
||||
color,
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="single-stat"
|
||||
style={{backgroundColor: bgColor, color: textColor}}
|
||||
>
|
||||
<div className="single-stat" style={singleStatStyles}>
|
||||
<span
|
||||
className={classnames('single-stat--value', {
|
||||
'single-stat--small': cellHeight === SMALL_CELL_HEIGHT,
|
||||
|
@ -77,6 +92,7 @@ SingleStat.propTypes = {
|
|||
prefix: string,
|
||||
suffix: string,
|
||||
lineGraph: bool,
|
||||
staticLegendHeight: number,
|
||||
}
|
||||
|
||||
export default SingleStat
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import _ from 'lodash'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import ReactTooltip from 'react-tooltip'
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import _ from 'lodash'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
import {removeMeasurement} from 'shared/graphs/helpers'
|
||||
|
||||
const staticLegendItemClassname = (visibilities, i, hoverEnabled) => {
|
||||
|
@ -65,13 +65,10 @@ class StaticLegend extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {dygraph} = this.props
|
||||
const {dygraphSeries} = this.props
|
||||
const {visibilities} = this.state
|
||||
|
||||
const labels = dygraph ? _.drop(dygraph.getLabels()) : []
|
||||
const colors = dygraph
|
||||
? _.map(labels, l => dygraph.attributes_.series_[l].options.color)
|
||||
: []
|
||||
const labels = _.keys(dygraphSeries)
|
||||
const colors = _.map(labels, l => dygraphSeries[l].color)
|
||||
|
||||
const hoverEnabled = labels.length > 1
|
||||
|
||||
|
@ -105,7 +102,7 @@ class StaticLegend extends Component {
|
|||
const {shape, func} = PropTypes
|
||||
|
||||
StaticLegend.propTypes = {
|
||||
sharedLegend: shape({}),
|
||||
dygraphSeries: shape({}),
|
||||
dygraph: shape({}),
|
||||
handleReceiveStaticLegendHeight: func.isRequired,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
class TagsAddButton extends Component {
|
||||
constructor(props) {
|
||||
|
|
|
@ -6,7 +6,7 @@ import OnClickOutside from 'shared/components/OnClickOutside'
|
|||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import CustomTimeRangeOverlay from 'shared/components/CustomTimeRangeOverlay'
|
||||
|
||||
import timeRanges from 'hson!shared/data/timeRanges.hson'
|
||||
import {timeRanges} from 'shared/data/timeRanges'
|
||||
import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index'
|
||||
|
||||
const dateFormat = 'YYYY-MM-DD HH:mm'
|
||||
|
|
|
@ -430,20 +430,6 @@ export const DEFAULT_SOURCE = {
|
|||
metaUrl: '',
|
||||
}
|
||||
|
||||
export const intervalValuesPoints = [
|
||||
{value: '333', type: 'points', selected: true},
|
||||
]
|
||||
|
||||
export const interval = {
|
||||
id: 'interval',
|
||||
type: 'autoGroupBy',
|
||||
tempVar: ':interval:',
|
||||
label: 'automatically determine the best group by time',
|
||||
values: intervalValuesPoints,
|
||||
}
|
||||
|
||||
export const TEMPLATES = [interval]
|
||||
|
||||
export const IS_STATIC_LEGEND = legend =>
|
||||
_.get(legend, 'type', false) === 'static'
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
[
|
||||
{milliseconds: 0, inputValue: 'Paused', menuOption: 'Paused'},
|
||||
{milliseconds: 5000, inputValue: 'Every 5 seconds', menuOption: 'Every 5 seconds'},
|
||||
{milliseconds: 10000, inputValue: 'Every 10 seconds', menuOption: 'Every 10 seconds'},
|
||||
{milliseconds: 15000, inputValue: 'Every 15 seconds', menuOption: 'Every 15 seconds'},
|
||||
{milliseconds: 30000, inputValue: 'Every 30 seconds', menuOption: 'Every 30 seconds'},
|
||||
{milliseconds: 60000, inputValue: 'Every 60 seconds', menuOption: 'Every 60 seconds'}
|
||||
]
|
|
@ -0,0 +1,30 @@
|
|||
const autoRefreshItems = [
|
||||
{milliseconds: 0, inputValue: 'Paused', menuOption: 'Paused'},
|
||||
{
|
||||
milliseconds: 5000,
|
||||
inputValue: 'Every 5 seconds',
|
||||
menuOption: 'Every 5 seconds',
|
||||
},
|
||||
{
|
||||
milliseconds: 10000,
|
||||
inputValue: 'Every 10 seconds',
|
||||
menuOption: 'Every 10 seconds',
|
||||
},
|
||||
{
|
||||
milliseconds: 15000,
|
||||
inputValue: 'Every 15 seconds',
|
||||
menuOption: 'Every 15 seconds',
|
||||
},
|
||||
{
|
||||
milliseconds: 30000,
|
||||
inputValue: 'Every 30 seconds',
|
||||
menuOption: 'Every 30 seconds',
|
||||
},
|
||||
{
|
||||
milliseconds: 60000,
|
||||
inputValue: 'Every 60 seconds',
|
||||
menuOption: 'Every 60 seconds',
|
||||
},
|
||||
]
|
||||
|
||||
export default autoRefreshItems
|
|
@ -1,9 +0,0 @@
|
|||
[
|
||||
{type: 'null', text: 'null', menuOption: '(null)', inputValue: 'null', isValidForKapaNodes: true},
|
||||
{type: 'previous', text: 'previous', menuOption: '(previous)', inputValue: 'previous', isValidForKapaNodes: false},
|
||||
{type: 'number', text: 'number', menuOption: '(number)', inputValue: '0', isValidForKapaNodes: true},
|
||||
{type: 'none', text: 'none', menuOption: '(none)', inputValue: 'none', isValidForKapaNodes: true},
|
||||
{type: 'linear', text: 'linear', menuOption: '(linear)', inputValue: 'linear', isValidForKapaNodes: false},
|
||||
]
|
||||
// for if/when filtering FillQuery queryFill types not supported as Kapacitor nodes:
|
||||
// queryFills.filter(fill => !(isKapacitorRule && !fill.isValidForKapaNodes))
|
|
@ -0,0 +1,41 @@
|
|||
const queryFills = [
|
||||
{
|
||||
type: 'null',
|
||||
text: 'null',
|
||||
menuOption: '(null)',
|
||||
inputValue: 'null',
|
||||
isValidForKapaNodes: true,
|
||||
},
|
||||
{
|
||||
type: 'previous',
|
||||
text: 'previous',
|
||||
menuOption: '(previous)',
|
||||
inputValue: 'previous',
|
||||
isValidForKapaNodes: false,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
text: 'number',
|
||||
menuOption: '(number)',
|
||||
inputValue: '0',
|
||||
isValidForKapaNodes: true,
|
||||
},
|
||||
{
|
||||
type: 'none',
|
||||
text: 'none',
|
||||
menuOption: '(none)',
|
||||
inputValue: 'none',
|
||||
isValidForKapaNodes: true,
|
||||
},
|
||||
{
|
||||
type: 'linear',
|
||||
text: 'linear',
|
||||
menuOption: '(linear)',
|
||||
inputValue: 'linear',
|
||||
isValidForKapaNodes: false,
|
||||
},
|
||||
]
|
||||
// for if/when filtering FillQuery queryFill types not supported as Kapacitor nodes:
|
||||
// queryFills.filter(fill => !(isKapacitorRule && !fill.isValidForKapaNodes))
|
||||
|
||||
export default queryFills
|
|
@ -1,26 +0,0 @@
|
|||
[
|
||||
{
|
||||
"id": "pastWeek",
|
||||
"name": "Past Week"
|
||||
},
|
||||
{
|
||||
"id": "pastMonth",
|
||||
"name": "Past Month"
|
||||
},
|
||||
{
|
||||
"id": "pastYear",
|
||||
"name": "Past Year"
|
||||
},
|
||||
{
|
||||
"id": "thisWeek",
|
||||
"name": "This Week"
|
||||
},
|
||||
{
|
||||
"id": "thisMonth",
|
||||
"name": "This Month"
|
||||
},
|
||||
{
|
||||
"id": "thisYear",
|
||||
"name": "This Year"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,28 @@
|
|||
const timeRangeDefaults = [
|
||||
{
|
||||
id: 'pastWeek',
|
||||
name: 'Past Week',
|
||||
},
|
||||
{
|
||||
id: 'pastMonth',
|
||||
name: 'Past Month',
|
||||
},
|
||||
{
|
||||
id: 'pastYear',
|
||||
name: 'Past Year',
|
||||
},
|
||||
{
|
||||
id: 'thisWeek',
|
||||
name: 'This Week',
|
||||
},
|
||||
{
|
||||
id: 'thisMonth',
|
||||
name: 'This Month',
|
||||
},
|
||||
{
|
||||
id: 'thisYear',
|
||||
name: 'This Year',
|
||||
},
|
||||
]
|
||||
|
||||
export default timeRangeDefaults
|
|
@ -1,11 +0,0 @@
|
|||
[
|
||||
{defaultGroupBy: '10s', seconds: 300, inputValue: 'Past 5 minutes', lower: 'now() - 5m', upper: null, menuOption: 'Past 5 minutes'},
|
||||
{defaultGroupBy: '1m', seconds: 900, inputValue: 'Past 15 minutes', lower: 'now() - 15m', upper: null, menuOption: 'Past 15 minutes'},
|
||||
{defaultGroupBy: '1m', seconds: 3600, inputValue: 'Past hour', lower: 'now() - 1h', upper: null, menuOption: 'Past hour'},
|
||||
{defaultGroupBy: '1m', seconds: 21600, inputValue: 'Past 6 hours', lower: 'now() - 6h', upper: null, menuOption: 'Past 6 hours'},
|
||||
{defaultGroupBy: '5m', seconds: 43200, inputValue: 'Past 12 hours', lower: 'now() - 12h', upper: null, menuOption: 'Past 12 hours'},
|
||||
{defaultGroupBy: '10m', seconds: 86400, inputValue: 'Past 24 hours', lower: 'now() - 24h', upper: null, menuOption: 'Past 24 hours'},
|
||||
{defaultGroupBy: '30m', seconds: 172800, inputValue: 'Past 2 days', lower: 'now() - 2d', upper: null, menuOption: 'Past 2 days'},
|
||||
{defaultGroupBy: '1h', seconds: 604800, inputValue: 'Past 7 days', lower: 'now() - 7d', upper: null, menuOption: 'Past 7 days'},
|
||||
{defaultGroupBy: '6h', seconds: 2592000, inputValue: 'Past 30 days', lower: 'now() - 30d', upper: null, menuOption: 'Past 30 days'},
|
||||
]
|
|
@ -0,0 +1,74 @@
|
|||
export const timeRanges = [
|
||||
{
|
||||
defaultGroupBy: '10s',
|
||||
seconds: 300,
|
||||
inputValue: 'Past 5 minutes',
|
||||
lower: 'now() - 5m',
|
||||
upper: null,
|
||||
menuOption: 'Past 5 minutes',
|
||||
},
|
||||
{
|
||||
defaultGroupBy: '1m',
|
||||
seconds: 900,
|
||||
inputValue: 'Past 15 minutes',
|
||||
lower: 'now() - 15m',
|
||||
upper: null,
|
||||
menuOption: 'Past 15 minutes',
|
||||
},
|
||||
{
|
||||
defaultGroupBy: '1m',
|
||||
seconds: 3600,
|
||||
inputValue: 'Past hour',
|
||||
lower: 'now() - 1h',
|
||||
upper: null,
|
||||
menuOption: 'Past hour',
|
||||
},
|
||||
{
|
||||
defaultGroupBy: '1m',
|
||||
seconds: 21600,
|
||||
inputValue: 'Past 6 hours',
|
||||
lower: 'now() - 6h',
|
||||
upper: null,
|
||||
menuOption: 'Past 6 hours',
|
||||
},
|
||||
{
|
||||
defaultGroupBy: '5m',
|
||||
seconds: 43200,
|
||||
inputValue: 'Past 12 hours',
|
||||
lower: 'now() - 12h',
|
||||
upper: null,
|
||||
menuOption: 'Past 12 hours',
|
||||
},
|
||||
{
|
||||
defaultGroupBy: '10m',
|
||||
seconds: 86400,
|
||||
inputValue: 'Past 24 hours',
|
||||
lower: 'now() - 24h',
|
||||
upper: null,
|
||||
menuOption: 'Past 24 hours',
|
||||
},
|
||||
{
|
||||
defaultGroupBy: '30m',
|
||||
seconds: 172800,
|
||||
inputValue: 'Past 2 days',
|
||||
lower: 'now() - 2d',
|
||||
upper: null,
|
||||
menuOption: 'Past 2 days',
|
||||
},
|
||||
{
|
||||
defaultGroupBy: '1h',
|
||||
seconds: 604800,
|
||||
inputValue: 'Past 7 days',
|
||||
lower: 'now() - 7d',
|
||||
upper: null,
|
||||
menuOption: 'Past 7 days',
|
||||
},
|
||||
{
|
||||
defaultGroupBy: '6h',
|
||||
seconds: 2592000,
|
||||
inputValue: 'Past 30 days',
|
||||
lower: 'now() - 30d',
|
||||
upper: null,
|
||||
menuOption: 'Past 30 days',
|
||||
},
|
||||
]
|
|
@ -17,8 +17,8 @@ export const timeRangeType = ({upper, lower, type}) => {
|
|||
return INVALID
|
||||
}
|
||||
|
||||
const isUpperValid = moment(upper).isValid()
|
||||
const isLowerValid = moment(lower).isValid()
|
||||
const isUpperValid = moment(new Date(upper)).isValid()
|
||||
const isLowerValid = moment(new Date(lower)).isValid()
|
||||
|
||||
// {lower: <Date>, upper: <Date>}
|
||||
if (isLowerValid && isUpperValid) {
|
||||
|
@ -103,8 +103,8 @@ const getMomentUnit = unit => {
|
|||
|
||||
export const shiftDate = (date, quantity, unit) => {
|
||||
if (!date && !quantity && !unit) {
|
||||
return moment(date)
|
||||
return moment(new Date(date))
|
||||
}
|
||||
|
||||
return moment(date).add(quantity, getMomentUnit(unit))
|
||||
return moment(new Date(date)).add(quantity, getMomentUnit(unit))
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {AUTOREFRESH_DEFAULT} from 'shared/constants'
|
||||
import timeRanges from 'hson!shared/data/timeRanges.hson'
|
||||
import {timeRanges} from 'shared/data/timeRanges'
|
||||
|
||||
import * as actionTypes from 'src/status/constants/actionTypes'
|
||||
|
||||
|
|
|
@ -93,6 +93,9 @@
|
|||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
.dygraph-child > .single-stat {
|
||||
z-index: 3;
|
||||
}
|
||||
.single-stat--value {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {buildQuery} from 'utils/influxql'
|
||||
import {TYPE_SHIFTED, TYPE_QUERY_CONFIG} from 'src/dashboards/constants'
|
||||
import timeRanges from 'hson!shared/data/timeRanges.hson'
|
||||
import {timeRanges} from 'shared/data/timeRanges'
|
||||
|
||||
const buildCannedDashboardQuery = (query, {lower, upper}, host) => {
|
||||
const {defaultGroupBy} = timeRanges.find(range => range.lower === lower) || {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import uuid from 'node-uuid'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import {NULL_STRING} from 'shared/constants/queryFillOptions'
|
||||
|
||||
|
|
|
@ -157,9 +157,9 @@ function _buildGroupByTime(groupBy) {
|
|||
return ''
|
||||
}
|
||||
|
||||
return ` GROUP BY time(${groupBy.time === AUTO_GROUP_BY
|
||||
return ` GROUP BY ${groupBy.time === AUTO_GROUP_BY
|
||||
? TEMP_VAR_INTERVAL
|
||||
: `${groupBy.time}`})`
|
||||
: `time(${groupBy.time})`}`
|
||||
}
|
||||
|
||||
function _buildGroupByTags(groupBy) {
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
const express = require('express')
|
||||
const request = require('request')
|
||||
const {default: storybook} = require('@kadira/storybook/dist/server/middleware')
|
||||
|
||||
const app = express()
|
||||
|
||||
const handler = (req, res) => {
|
||||
console.log(`${req.method} ${req.url}`)
|
||||
const url = 'http://localhost:8888' + req.url
|
||||
req.pipe(request(url)).pipe(res)
|
||||
}
|
||||
|
||||
app.use(storybook('./.storybook'))
|
||||
app.get('/chronograf/v1/*', handler)
|
||||
app.post('/chronograf/v1/*', handler)
|
||||
|
||||
app.listen(6006, () => {
|
||||
console.log('storybook proxy server now running')
|
||||
})
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue