Merge branch 'master' into goodbye-bootstrap
commit
b02a79f0e4
|
@ -1,5 +1,5 @@
|
||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 1.4.1.3
|
current_version = 1.4.2.1
|
||||||
files = README.md server/swagger.json
|
files = README.md server/swagger.json
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.(?P<release>\d+)
|
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.(?P<release>\d+)
|
||||||
serialize = {major}.{minor}.{patch}.{release}
|
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
|
### 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. [#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
|
### 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. [#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
|
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. [#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. [#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. [#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. [#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
|
1. [#2819](https://github.com/influxdata/chronograf/pull/2819): Deprecate --new-sources in CLI
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ option.
|
||||||
## Versions
|
## Versions
|
||||||
|
|
||||||
The most recent version of Chronograf is
|
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
|
Spotted a bug or have a feature request? Please open
|
||||||
[an issue](https://github.com/influxdata/chronograf/issues/new)!
|
[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:
|
To get started right away with Docker, you can pull down our latest release:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker pull chronograf:1.4.1.3
|
docker pull chronograf:1.4.2.1
|
||||||
```
|
```
|
||||||
|
|
||||||
### From Source
|
### From Source
|
||||||
|
|
|
@ -12,10 +12,6 @@ import (
|
||||||
// SortTemplates the templates by size, then type, then value.
|
// SortTemplates the templates by size, then type, then value.
|
||||||
func SortTemplates(ts []chronograf.TemplateVar) []chronograf.TemplateVar {
|
func SortTemplates(ts []chronograf.TemplateVar) []chronograf.TemplateVar {
|
||||||
sort.Slice(ts, func(i, j int) bool {
|
sort.Slice(ts, func(i, j int) bool {
|
||||||
if ts[i].Var == ":interval:" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ts[i].Values) != len(ts[j].Values) {
|
if len(ts[i].Values) != len(ts[j].Values) {
|
||||||
return 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
|
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 {
|
if res, ok := tv["resolution"]; ok {
|
||||||
resolution, err := strconv.ParseInt(res, 0, 64)
|
resolution, err := strconv.ParseInt(res, 0, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -101,22 +83,6 @@ func RenderTemplate(query string, t chronograf.TemplateVar, now time.Time) (stri
|
||||||
return query, nil
|
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
|
// AutoGroupBy generates the time to group by in order to decimate the number of
|
||||||
// points returned in a query
|
// points returned in a query
|
||||||
func AutoGroupBy(resolution, pixelsPerPoint int64, duration time.Duration) string {
|
func AutoGroupBy(resolution, pixelsPerPoint int64, duration time.Duration) string {
|
||||||
|
|
|
@ -125,38 +125,6 @@ func TestTemplateReplace(t *testing.T) {
|
||||||
},
|
},
|
||||||
want: `SELECT :field: FROM "cpu"`,
|
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",
|
name: "auto group by",
|
||||||
query: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by :interval:`,
|
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:",
|
Var: ":interval:",
|
||||||
Values: []chronograf.TemplateValue{
|
Values: []chronograf.TemplateValue{
|
||||||
{
|
{
|
||||||
Value: "999",
|
Value: "1000",
|
||||||
Type: "resolution",
|
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",
|
name: "auto group by without duration",
|
||||||
|
@ -185,7 +153,7 @@ func TestTemplateReplace(t *testing.T) {
|
||||||
Var: ":interval:",
|
Var: ":interval:",
|
||||||
Values: []chronograf.TemplateValue{
|
Values: []chronograf.TemplateValue{
|
||||||
{
|
{
|
||||||
Value: "999",
|
Value: "1000",
|
||||||
Type: "resolution",
|
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:",
|
name: "auto group by with :dashboardTime:",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"info": {
|
"info": {
|
||||||
"title": "Chronograf",
|
"title": "Chronograf",
|
||||||
"description": "API endpoints for Chronograf",
|
"description": "API endpoints for Chronograf",
|
||||||
"version": "1.4.1.3"
|
"version": "1.4.2.1"
|
||||||
},
|
},
|
||||||
"schemes": ["http"],
|
"schemes": ["http"],
|
||||||
"basePath": "/chronograf/v1",
|
"basePath": "/chronograf/v1",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"transform-runtime",
|
"transform-runtime",
|
||||||
"lodash"
|
"lodash"
|
||||||
],
|
],
|
||||||
"presets": ["es2015", "react", "stage-0"],
|
"presets": ["env", "react", "stage-0"],
|
||||||
"env": {
|
"env": {
|
||||||
"production": {
|
"production": {
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
|
14
ui/.eslintrc
14
ui/.eslintrc
|
@ -1,20 +1,26 @@
|
||||||
{
|
{
|
||||||
parser: 'babel-eslint',
|
"parser": "babel-eslint",
|
||||||
plugins: [
|
plugins: [
|
||||||
'react',
|
'react',
|
||||||
'prettier',
|
'prettier',
|
||||||
'babel',
|
'babel',
|
||||||
|
'jest',
|
||||||
|
],
|
||||||
|
extends: [
|
||||||
|
"prettier",
|
||||||
|
"prettier/react"
|
||||||
],
|
],
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
mocha: true,
|
mocha: true,
|
||||||
|
"jest": true,
|
||||||
node: true,
|
node: true,
|
||||||
es6: true,
|
es6: true,
|
||||||
},
|
},
|
||||||
globals: {
|
globals: {
|
||||||
expect: true,
|
expect: true,
|
||||||
},
|
},
|
||||||
"parserOptions": {
|
parserOptions: {
|
||||||
ecmaFeatures: {
|
ecmaFeatures: {
|
||||||
arrowFunctions: true,
|
arrowFunctions: true,
|
||||||
binaryLiterals: true,
|
binaryLiterals: true,
|
||||||
|
@ -244,6 +250,10 @@
|
||||||
'semi': false,
|
'semi': false,
|
||||||
}],
|
}],
|
||||||
|
|
||||||
|
// jest
|
||||||
|
'jest/no-disabled-tests': "warn",
|
||||||
|
'jest/no-focused-tests': "error",
|
||||||
|
|
||||||
// Babel
|
// Babel
|
||||||
'babel/no-invalid-this': 1
|
'babel/no-invalid-this': 1
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,3 +6,4 @@ dist/
|
||||||
bower_components/
|
bower_components/
|
||||||
log/
|
log/
|
||||||
.tern-project
|
.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')
|
const webpack = require('webpack')
|
||||||
var path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
module.exports = function(config) {
|
module.exports = function(config) {
|
||||||
config.set({
|
config.set({
|
||||||
browsers: ['PhantomJS'],
|
browsers: ['PhantomJS'],
|
||||||
singleRun: true,
|
frameworks: ['mocha'],
|
||||||
frameworks: ['mocha', 'sinon-chai'],
|
|
||||||
files: [
|
files: [
|
||||||
'node_modules/babel-polyfill/dist/polyfill.js',
|
'node_modules/babel-polyfill/dist/polyfill.js',
|
||||||
'spec/spec-helper.js',
|
'spec/spec-helper.js',
|
||||||
|
@ -42,10 +41,6 @@ module.exports = function(config) {
|
||||||
test: /sinon\/pkg\/sinon\.js/,
|
test: /sinon\/pkg\/sinon\.js/,
|
||||||
loader: 'imports?define=>false,require=>false',
|
loader: 'imports?define=>false,require=>false',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
test: /\.json$/,
|
|
||||||
loader: 'json',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
externals: {
|
externals: {
|
||||||
|
@ -61,7 +56,6 @@ module.exports = function(config) {
|
||||||
shared: path.resolve(__dirname, 'src', 'shared'),
|
shared: path.resolve(__dirname, 'src', 'shared'),
|
||||||
style: path.resolve(__dirname, 'src', 'style'),
|
style: path.resolve(__dirname, 'src', 'style'),
|
||||||
utils: path.resolve(__dirname, 'src', 'utils'),
|
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",
|
"name": "chronograf-ui",
|
||||||
"version": "1.4.1-3",
|
"version": "1.4.2-1",
|
||||||
"private": false,
|
"private": false,
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
|
@ -9,17 +9,19 @@
|
||||||
"url": "github:influxdata/chronograf"
|
"url": "github:influxdata/chronograf"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn run clean && env NODE_ENV=production webpack --optimize-minimize --config ./webpack/prodConfig.js",
|
"build": "yarn run clean && webpack --config ./webpack/prod.config.js",
|
||||||
"build:dev": "webpack --config ./webpack/devConfig.js",
|
"build:dev": "webpack --config ./webpack/dev.config.js",
|
||||||
"start": "yarn run clean && webpack --watch --config ./webpack/devConfig.js",
|
"build:vendor": "webpack --config webpack/vendor.config.js",
|
||||||
"start:hmr": "webpack-dev-server --open --config ./webpack/devConfig.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/",
|
"lint": "esw src/",
|
||||||
"test": "karma start",
|
"test": "jest --runInBand",
|
||||||
"test:integration": "nightwatch tests --skip",
|
|
||||||
"test:lint": "yarn run lint; yarn run test",
|
"test:lint": "yarn run lint; yarn run test",
|
||||||
"test:dev": "concurrently \"yarn run lint --watch\" \"yarn run test --no-single-run --reporters=verbose\"",
|
"test:watch": "jest --watch",
|
||||||
"clean": "rm -rf build/*",
|
"clean": "rm -rf ./build/*",
|
||||||
"prettier": "prettier --single-quote --trailing-comma es5 --bracket-spacing false --semi false --write \"{src,spec}/**/*.js\"; eslint src --fix"
|
"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": "",
|
"author": "",
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
@ -28,10 +30,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"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",
|
"autoprefixer": "^6.3.1",
|
||||||
"babel-core": "^6.5.1",
|
"babel-core": "^6.5.1",
|
||||||
"babel-eslint": "6.1.2",
|
"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-lodash": "^2.0.1",
|
||||||
"babel-plugin-syntax-trailing-function-commas": "^6.5.0",
|
"babel-plugin-syntax-trailing-function-commas": "^6.5.0",
|
||||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
"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-react-remove-prop-types": "^0.2.1",
|
||||||
"babel-plugin-transform-runtime": "^6.5.0",
|
"babel-plugin-transform-runtime": "^6.5.0",
|
||||||
"babel-polyfill": "^6.13.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-react": "^6.5.0",
|
||||||
"babel-preset-stage-0": "^6.16.0",
|
"babel-preset-stage-0": "^6.16.0",
|
||||||
"babel-runtime": "^6.5.0",
|
"babel-runtime": "^6.5.0",
|
||||||
"bower": "^1.7.7",
|
"bower": "^1.7.7",
|
||||||
"chai": "^3.5.0",
|
"compression-webpack-plugin": "^1.1.8",
|
||||||
"concurrently": "^3.5.0",
|
"concurrently": "^3.5.0",
|
||||||
"core-js": "^2.1.3",
|
"core-js": "^2.1.3",
|
||||||
"css-loader": "^0.23.1",
|
"css-loader": "^0.23.1",
|
||||||
"envify": "^3.4.0",
|
"envify": "^3.4.0",
|
||||||
"enzyme": "^2.4.1",
|
"enzyme": "^3.3.0",
|
||||||
|
"enzyme-adapter-react-15": "^1.0.5",
|
||||||
"eslint": "^3.14.1",
|
"eslint": "^3.14.1",
|
||||||
|
"eslint-config-prettier": "^2.9.0",
|
||||||
"eslint-loader": "1.6.1",
|
"eslint-loader": "1.6.1",
|
||||||
|
"eslint-plugin-jest": "^21.12.2",
|
||||||
"eslint-plugin-prettier": "^2.1.2",
|
"eslint-plugin-prettier": "^2.1.2",
|
||||||
"eslint-plugin-react": "6.6.0",
|
"eslint-plugin-react": "6.6.0",
|
||||||
"eslint-watch": "^3.1.2",
|
"eslint-watch": "^3.1.2",
|
||||||
"express": "^4.14.0",
|
"express": "^4.14.0",
|
||||||
"extract-text-webpack-plugin": "^1.0.1",
|
"extract-text-webpack-plugin": "^3.0.2",
|
||||||
"file-loader": "^0.8.5",
|
"file-loader": "^1.1.7",
|
||||||
|
"fork-ts-checker-webpack-plugin": "^0.3.0",
|
||||||
"hanson": "^1.1.1",
|
"hanson": "^1.1.1",
|
||||||
"hson-loader": "^1.0.0",
|
"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",
|
"imports-loader": "^0.6.5",
|
||||||
|
"jest": "^22.4.2",
|
||||||
|
"jest-runner-eslint": "^0.4.0",
|
||||||
"jsdom": "^9.0.0",
|
"jsdom": "^9.0.0",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.7",
|
||||||
"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",
|
|
||||||
"mustache": "^2.2.1",
|
"mustache": "^2.2.1",
|
||||||
"node-sass": "^4.5.3",
|
"node-sass": "^4.5.3",
|
||||||
"on-build-webpack": "^0.1.0",
|
"on-build-webpack": "^0.1.0",
|
||||||
|
@ -82,17 +87,21 @@
|
||||||
"postcss-loader": "^0.8.0",
|
"postcss-loader": "^0.8.0",
|
||||||
"postcss-reporter": "^1.3.1",
|
"postcss-reporter": "^1.3.1",
|
||||||
"precss": "^1.4.0",
|
"precss": "^1.4.0",
|
||||||
"prettier": "^1.5.3",
|
"prettier": "1.5.3",
|
||||||
"react-addons-test-utils": "^15.0.2",
|
"react-addons-test-utils": "^15.0.2",
|
||||||
"resolve-url-loader": "^1.6.0",
|
"react-test-renderer": "^15.6.1",
|
||||||
"sass-loader": "^3.2.0",
|
"resolve-url-loader": "^2.2.1",
|
||||||
"sinon": "^1.17.4",
|
"sass-loader": "^6.0.6",
|
||||||
"sinon-chai": "^2.8.0",
|
|
||||||
"style-loader": "^0.13.0",
|
"style-loader": "^0.13.0",
|
||||||
"testem": "^1.2.1",
|
"testem": "^1.2.1",
|
||||||
"uglify-js": "^2.6.1",
|
"thread-loader": "^1.1.4",
|
||||||
"webpack": "^1.13.0",
|
"ts-loader": "^3.5.0",
|
||||||
"webpack-dev-server": "^1.14.1"
|
"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": {
|
"dependencies": {
|
||||||
"@skidding/react-codemirror": "^1.0.1",
|
"@skidding/react-codemirror": "^1.0.1",
|
||||||
|
@ -108,7 +117,7 @@
|
||||||
"lodash": "^4.3.0",
|
"lodash": "^4.3.0",
|
||||||
"moment": "^2.13.0",
|
"moment": "^2.13.0",
|
||||||
"nano-date": "^2.0.1",
|
"nano-date": "^2.0.1",
|
||||||
"node-uuid": "^1.4.7",
|
"prop-types": "^15.6.1",
|
||||||
"query-string": "^5.0.0",
|
"query-string": "^5.0.0",
|
||||||
"react": "^15.0.2",
|
"react": "^15.0.2",
|
||||||
"react-addons-shallow-compare": "^15.0.2",
|
"react-addons-shallow-compare": "^15.0.2",
|
||||||
|
@ -122,12 +131,12 @@
|
||||||
"react-redux": "^4.4.0",
|
"react-redux": "^4.4.0",
|
||||||
"react-router": "^3.0.2",
|
"react-router": "^3.0.2",
|
||||||
"react-router-redux": "^4.0.8",
|
"react-router-redux": "^4.0.8",
|
||||||
"react-sparklines": "^1.4.2",
|
|
||||||
"react-tooltip": "^3.2.1",
|
"react-tooltip": "^3.2.1",
|
||||||
"redux": "^3.3.1",
|
"redux": "^3.3.1",
|
||||||
"redux-auth-wrapper": "^1.0.0",
|
"redux-auth-wrapper": "^1.0.0",
|
||||||
"redux-thunk": "^1.0.3",
|
"redux-thunk": "^1.0.3",
|
||||||
"rome": "^2.1.22",
|
"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 _ from 'lodash'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getUsers as getUsersAJAX,
|
getUsers as getUsersAJAX,
|
||||||
|
|
|
@ -9,18 +9,13 @@ class ChangePassRow extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
showForm: false,
|
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})
|
this.setState({showForm: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancel() {
|
handleCancel = () => {
|
||||||
this.setState({showForm: false})
|
this.setState({showForm: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,12 +23,12 @@ class ChangePassRow extends Component {
|
||||||
this.setState({showForm: false})
|
this.setState({showForm: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(user) {
|
handleSubmit = user => {
|
||||||
this.props.onApply(user)
|
this.props.onApply(user)
|
||||||
this.setState({showForm: false})
|
this.setState({showForm: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyPress(user) {
|
handleKeyPress = user => {
|
||||||
return e => {
|
return e => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
this.handleSubmit(user)
|
this.handleSubmit(user)
|
||||||
|
@ -41,7 +36,7 @@ class ChangePassRow extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEdit(user) {
|
handleEdit = user => {
|
||||||
return e => {
|
return e => {
|
||||||
this.props.onEdit(user, {[e.target.name]: e.target.value})
|
this.props.onEdit(user, {[e.target.name]: e.target.value})
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,24 +7,20 @@ class QueryRow extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.handleInitiateKill = ::this.handleInitiateKill
|
|
||||||
this.handleFinishHim = ::this.handleFinishHim
|
|
||||||
this.handleShowMercy = ::this.handleShowMercy
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
confirmingKill: false,
|
confirmingKill: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInitiateKill() {
|
handleInitiateKill = () => {
|
||||||
this.setState({confirmingKill: true})
|
this.setState({confirmingKill: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFinishHim() {
|
handleFinishHim = () => {
|
||||||
this.props.onKill(this.props.query.id)
|
this.props.onKill(this.props.query.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowMercy() {
|
handleShowMercy = () => {
|
||||||
this.setState({confirmingKill: false})
|
this.setState({confirmingKill: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,9 @@ import {ROLES_TABLE} from 'src/admin/constants/tableSizing'
|
||||||
class RoleEditingRow extends Component {
|
class RoleEditingRow extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.handleKeyPress = ::this.handleKeyPress
|
|
||||||
this.handleEdit = ::this.handleEdit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyPress(role) {
|
handleKeyPress = role => {
|
||||||
return e => {
|
return e => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
this.props.onSave(role)
|
this.props.onSave(role)
|
||||||
|
@ -18,7 +15,7 @@ class RoleEditingRow extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEdit(role) {
|
handleEdit = role => {
|
||||||
return e => {
|
return e => {
|
||||||
this.props.onEdit(role, {[e.target.name]: e.target.value})
|
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 {
|
class UserEditName extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.handleKeyPress = ::this.handleKeyPress
|
|
||||||
this.handleEdit = ::this.handleEdit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyPress(user) {
|
handleKeyPress = user => {
|
||||||
return e => {
|
return e => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
this.props.onSave(user)
|
this.props.onSave(user)
|
||||||
|
@ -18,7 +15,7 @@ class UserEditName extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEdit(user) {
|
handleEdit = user => {
|
||||||
return e => {
|
return e => {
|
||||||
this.props.onEdit(user, {[e.target.name]: e.target.value})
|
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'
|
import {USERS_TABLE} from 'src/admin/constants/tableSizing'
|
||||||
|
|
||||||
class UserNewPassword extends Component {
|
class UserNewPassword extends Component {
|
||||||
constructor(props) {
|
handleKeyPress = user => {
|
||||||
super(props)
|
|
||||||
|
|
||||||
this.handleKeyPress = ::this.handleKeyPress
|
|
||||||
this.handleEdit = ::this.handleEdit
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeyPress(user) {
|
|
||||||
return e => {
|
return e => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
this.props.onSave(user)
|
this.props.onSave(user)
|
||||||
|
@ -18,7 +11,7 @@ class UserNewPassword extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEdit(user) {
|
handleEdit = user => {
|
||||||
return e => {
|
return e => {
|
||||||
this.props.onEdit(user, {[e.target.name]: e.target.value})
|
this.props.onEdit(user, {[e.target.name]: e.target.value})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {Component, PropTypes} from 'react'
|
import React, {Component, PropTypes} from 'react'
|
||||||
|
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import AllUsersTableHeader from 'src/admin/components/chronograf/AllUsersTableHeader'
|
import AllUsersTableHeader from 'src/admin/components/chronograf/AllUsersTableHeader'
|
||||||
import AllUsersTableRowNew from 'src/admin/components/chronograf/AllUsersTableRowNew'
|
import AllUsersTableRowNew from 'src/admin/components/chronograf/AllUsersTableRowNew'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {Component, PropTypes} from 'react'
|
import React, {Component, PropTypes} from 'react'
|
||||||
|
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import OrganizationsTableRow from 'src/admin/components/chronograf/OrganizationsTableRow'
|
import OrganizationsTableRow from 'src/admin/components/chronograf/OrganizationsTableRow'
|
||||||
import OrganizationsTableRowNew from 'src/admin/components/chronograf/OrganizationsTableRowNew'
|
import OrganizationsTableRowNew from 'src/admin/components/chronograf/OrganizationsTableRowNew'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {Component, PropTypes} from 'react'
|
import React, {Component, PropTypes} from 'react'
|
||||||
|
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
import ProvidersTableRow from 'src/admin/components/chronograf/ProvidersTableRow'
|
import ProvidersTableRow from 'src/admin/components/chronograf/ProvidersTableRow'
|
||||||
import ProvidersTableRowNew from 'src/admin/components/chronograf/ProvidersTableRowNew'
|
import ProvidersTableRowNew from 'src/admin/components/chronograf/ProvidersTableRowNew'
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {Component, PropTypes} from 'react'
|
import React, {Component, PropTypes} from 'react'
|
||||||
|
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import UsersTableHeader from 'src/admin/components/chronograf/UsersTableHeader'
|
import UsersTableHeader from 'src/admin/components/chronograf/UsersTableHeader'
|
||||||
import UsersTableRowNew from 'src/admin/components/chronograf/UsersTableRowNew'
|
import UsersTableRowNew from 'src/admin/components/chronograf/UsersTableRowNew'
|
||||||
|
|
|
@ -20,12 +20,6 @@ import {
|
||||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||||
|
|
||||||
class QueriesPage extends Component {
|
class QueriesPage extends Component {
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
this.updateQueries = ::this.updateQueries
|
|
||||||
this.handleKillQuery = ::this.handleKillQuery
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.updateQueries()
|
this.updateQueries()
|
||||||
const updateInterval = 5000
|
const updateInterval = 5000
|
||||||
|
@ -42,7 +36,7 @@ class QueriesPage extends Component {
|
||||||
return <QueriesTable queries={queries} onKillQuery={this.handleKillQuery} />
|
return <QueriesTable queries={queries} onKillQuery={this.handleKillQuery} />
|
||||||
}
|
}
|
||||||
|
|
||||||
updateQueries() {
|
updateQueries = () => {
|
||||||
const {source, notify, loadQueries} = this.props
|
const {source, notify, loadQueries} = this.props
|
||||||
showDatabases(source.links.proxy).then(resp => {
|
showDatabases(source.links.proxy).then(resp => {
|
||||||
const {databases, errors} = showDatabasesParser(resp.data)
|
const {databases, errors} = showDatabasesParser(resp.data)
|
||||||
|
@ -78,7 +72,7 @@ class QueriesPage extends Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKillQuery(id) {
|
handleKillQuery = id => {
|
||||||
const {source, killQuery} = this.props
|
const {source, killQuery} = this.props
|
||||||
killQuery(source.links.proxy, id)
|
killQuery(source.links.proxy, id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
NEW_DEFAULT_DATABASE,
|
NEW_DEFAULT_DATABASE,
|
||||||
NEW_EMPTY_RP,
|
NEW_EMPTY_RP,
|
||||||
} from 'src/admin/constants'
|
} from 'src/admin/constants'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
users: null,
|
users: null,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import React, {Component, PropTypes} from 'react'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import {Link} from 'react-router'
|
import {Link} from 'react-router'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import InfiniteScroll from 'shared/components/InfiniteScroll'
|
import InfiniteScroll from 'shared/components/InfiniteScroll'
|
||||||
|
|
||||||
|
@ -243,9 +243,6 @@ class SearchBar extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
searchTerm: '',
|
searchTerm: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleSearch = ::this.handleSearch
|
|
||||||
this.handleChange = ::this.handleChange
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
|
@ -253,11 +250,11 @@ class SearchBar extends Component {
|
||||||
this.handleSearch = _.debounce(this.handleSearch, waitPeriod)
|
this.handleSearch = _.debounce(this.handleSearch, waitPeriod)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSearch() {
|
handleSearch = () => {
|
||||||
this.props.onSearch(this.state.searchTerm)
|
this.props.onSearch(this.state.searchTerm)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange(e) {
|
handleChange = e => {
|
||||||
this.setState({searchTerm: e.target.value}, this.handleSearch)
|
this.setState({searchTerm: e.target.value}, this.handleSearch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import AJAX from 'utils/ajax'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
|
||||||
import timeRanges from 'hson!shared/data/timeRanges.hson'
|
import {timeRanges} from 'shared/data/timeRanges'
|
||||||
|
|
||||||
class AlertsApp extends Component {
|
class AlertsApp extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -35,11 +35,6 @@ class AlertsApp extends Component {
|
||||||
limitMultiplier: 1, // only used if AlertsApp receives a limit prop
|
limitMultiplier: 1, // only used if AlertsApp receives a limit prop
|
||||||
isAlertsMaxedOut: false, // 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
|
// 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(
|
getAlerts(
|
||||||
this.props.source.links.proxy,
|
this.props.source.links.proxy,
|
||||||
this.state.timeRange,
|
this.state.timeRange,
|
||||||
|
@ -112,13 +107,13 @@ class AlertsApp extends Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleGetMoreAlerts() {
|
handleGetMoreAlerts = () => {
|
||||||
this.setState({limitMultiplier: this.state.limitMultiplier + 1}, () => {
|
this.setState({limitMultiplier: this.state.limitMultiplier + 1}, () => {
|
||||||
this.fetchAlerts(this.state.limitMultiplier)
|
this.fetchAlerts(this.state.limitMultiplier)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSubComponents() {
|
renderSubComponents = () => {
|
||||||
const {source, isWidget, limit} = this.props
|
const {source, isWidget, limit} = this.props
|
||||||
const {isAlertsMaxedOut, alerts} = this.state
|
const {isAlertsMaxedOut, alerts} = this.state
|
||||||
|
|
||||||
|
@ -135,7 +130,7 @@ class AlertsApp extends Component {
|
||||||
: <NoKapacitorError source={source} />
|
: <NoKapacitorError source={source} />
|
||||||
}
|
}
|
||||||
|
|
||||||
handleApplyTime(timeRange) {
|
handleApplyTime = timeRange => {
|
||||||
this.setState({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 {connect} from 'react-redux'
|
||||||
import {withRouter} from 'react-router'
|
import {withRouter} from 'react-router'
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {Component, PropTypes} from 'react'
|
import React, {Component, PropTypes} from 'react'
|
||||||
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import ResizeContainer from 'shared/components/ResizeContainer'
|
import ResizeContainer from 'shared/components/ResizeContainer'
|
||||||
import QueryMaker from 'src/dashboards/components/QueryMaker'
|
import QueryMaker from 'src/dashboards/components/QueryMaker'
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {connect} from 'react-redux'
|
||||||
import {bindActionCreators} from 'redux'
|
import {bindActionCreators} from 'redux'
|
||||||
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||||
import Threshold from 'src/dashboards/components/Threshold'
|
import Threshold from 'src/dashboards/components/Threshold'
|
||||||
|
@ -46,7 +46,12 @@ class GaugeOptions extends Component {
|
||||||
name: GAUGE_COLORS[randomColor].name,
|
name: GAUGE_COLORS[randomColor].name,
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUpdateGaugeColors([...gaugeColors, newThreshold])
|
const updatedColors = _.sortBy(
|
||||||
|
[...gaugeColors, newThreshold],
|
||||||
|
color => color.value
|
||||||
|
)
|
||||||
|
|
||||||
|
handleUpdateGaugeColors(updatedColors)
|
||||||
} else {
|
} else {
|
||||||
onResetFocus()
|
onResetFocus()
|
||||||
}
|
}
|
||||||
|
@ -57,8 +62,9 @@ class GaugeOptions extends Component {
|
||||||
const gaugeColors = this.props.gaugeColors.filter(
|
const gaugeColors = this.props.gaugeColors.filter(
|
||||||
color => color.id !== threshold.id
|
color => color.id !== threshold.id
|
||||||
)
|
)
|
||||||
|
const sortedColors = _.sortBy(gaugeColors, color => color.value)
|
||||||
|
|
||||||
handleUpdateGaugeColors(gaugeColors)
|
handleUpdateGaugeColors(sortedColors)
|
||||||
onResetFocus()
|
onResetFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,12 +144,18 @@ class GaugeOptions extends Component {
|
||||||
handleUpdateAxes(newAxes)
|
handleUpdateAxes(newAxes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSortColors = () => {
|
||||||
|
const {gaugeColors, handleUpdateGaugeColors} = this.props
|
||||||
|
const sortedColors = _.sortBy(gaugeColors, color => color.value)
|
||||||
|
|
||||||
|
handleUpdateGaugeColors(sortedColors)
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {gaugeColors, axes: {y: {prefix, suffix}}} = this.props
|
const {gaugeColors, axes: {y: {prefix, suffix}}} = this.props
|
||||||
|
|
||||||
const disableMaxColor = gaugeColors.length > MIN_THRESHOLDS
|
const disableMaxColor = gaugeColors.length > MIN_THRESHOLDS
|
||||||
const disableAddThreshold = gaugeColors.length > MAX_THRESHOLDS
|
const disableAddThreshold = gaugeColors.length > MAX_THRESHOLDS
|
||||||
const sortedColors = _.sortBy(gaugeColors, color => color.value)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FancyScrollbar
|
<FancyScrollbar
|
||||||
|
@ -160,11 +172,11 @@ class GaugeOptions extends Component {
|
||||||
>
|
>
|
||||||
<span className="icon plus" /> Add Threshold
|
<span className="icon plus" /> Add Threshold
|
||||||
</button>
|
</button>
|
||||||
{sortedColors.map(color =>
|
{gaugeColors.map(color =>
|
||||||
<Threshold
|
<Threshold
|
||||||
isMin={color.value === sortedColors[0].value}
|
isMin={color.value === gaugeColors[0].value}
|
||||||
isMax={
|
isMax={
|
||||||
color.value === sortedColors[sortedColors.length - 1].value
|
color.value === gaugeColors[gaugeColors.length - 1].value
|
||||||
}
|
}
|
||||||
visualizationType="gauge"
|
visualizationType="gauge"
|
||||||
threshold={color}
|
threshold={color}
|
||||||
|
@ -174,6 +186,7 @@ class GaugeOptions extends Component {
|
||||||
onValidateColorValue={this.handleValidateColorValue}
|
onValidateColorValue={this.handleValidateColorValue}
|
||||||
onUpdateColorValue={this.handleUpdateColorValue}
|
onUpdateColorValue={this.handleUpdateColorValue}
|
||||||
onDeleteThreshold={this.handleDeleteThreshold}
|
onDeleteThreshold={this.handleDeleteThreshold}
|
||||||
|
onSortColors={this.handleSortColors}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {connect} from 'react-redux'
|
||||||
import {bindActionCreators} from 'redux'
|
import {bindActionCreators} from 'redux'
|
||||||
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||||
import Threshold from 'src/dashboards/components/Threshold'
|
import Threshold from 'src/dashboards/components/Threshold'
|
||||||
|
@ -67,18 +67,23 @@ class SingleStatOptions extends Component {
|
||||||
name: GAUGE_COLORS[randomColor].name,
|
name: GAUGE_COLORS[randomColor].name,
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUpdateSingleStatColors([...singleStatColors, newThreshold])
|
const updatedColors = _.sortBy(
|
||||||
|
[...singleStatColors, newThreshold],
|
||||||
|
color => color.value
|
||||||
|
)
|
||||||
|
|
||||||
|
handleUpdateSingleStatColors(updatedColors)
|
||||||
onResetFocus()
|
onResetFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteThreshold = threshold => () => {
|
handleDeleteThreshold = threshold => () => {
|
||||||
const {handleUpdateSingleStatColors, onResetFocus} = this.props
|
const {handleUpdateSingleStatColors, onResetFocus} = this.props
|
||||||
|
|
||||||
const singleStatColors = this.props.singleStatColors.filter(
|
const singleStatColors = this.props.singleStatColors.filter(
|
||||||
color => color.id !== threshold.id
|
color => color.id !== threshold.id
|
||||||
)
|
)
|
||||||
|
const sortedColors = _.sortBy(singleStatColors, color => color.value)
|
||||||
|
|
||||||
handleUpdateSingleStatColors(singleStatColors)
|
handleUpdateSingleStatColors(sortedColors)
|
||||||
onResetFocus()
|
onResetFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +131,13 @@ class SingleStatOptions extends Component {
|
||||||
handleUpdateAxes(newAxes)
|
handleUpdateAxes(newAxes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSortColors = () => {
|
||||||
|
const {singleStatColors, handleUpdateSingleStatColors} = this.props
|
||||||
|
const sortedColors = _.sortBy(singleStatColors, color => color.value)
|
||||||
|
|
||||||
|
handleUpdateSingleStatColors(sortedColors)
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
singleStatColors,
|
singleStatColors,
|
||||||
|
@ -135,8 +147,6 @@ class SingleStatOptions extends Component {
|
||||||
|
|
||||||
const disableAddThreshold = singleStatColors.length > MAX_THRESHOLDS
|
const disableAddThreshold = singleStatColors.length > MAX_THRESHOLDS
|
||||||
|
|
||||||
const sortedColors = _.sortBy(singleStatColors, color => color.value)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FancyScrollbar
|
<FancyScrollbar
|
||||||
className="display-options--cell y-axis-controls"
|
className="display-options--cell y-axis-controls"
|
||||||
|
@ -152,7 +162,7 @@ class SingleStatOptions extends Component {
|
||||||
>
|
>
|
||||||
<span className="icon plus" /> Add Threshold
|
<span className="icon plus" /> Add Threshold
|
||||||
</button>
|
</button>
|
||||||
{sortedColors.map(
|
{singleStatColors.map(
|
||||||
color =>
|
color =>
|
||||||
color.id === SINGLE_STAT_BASE
|
color.id === SINGLE_STAT_BASE
|
||||||
? <div className="gauge-controls--section" key={color.id}>
|
? <div className="gauge-controls--section" key={color.id}>
|
||||||
|
@ -172,6 +182,7 @@ class SingleStatOptions extends Component {
|
||||||
onValidateColorValue={this.handleValidateColorValue}
|
onValidateColorValue={this.handleValidateColorValue}
|
||||||
onUpdateColorValue={this.handleUpdateColorValue}
|
onUpdateColorValue={this.handleUpdateColorValue}
|
||||||
onDeleteThreshold={this.handleDeleteThreshold}
|
onDeleteThreshold={this.handleDeleteThreshold}
|
||||||
|
onSortColors={this.handleSortColors}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -29,6 +29,13 @@ class Threshold extends Component {
|
||||||
|
|
||||||
handleBlur = () => {
|
handleBlur = () => {
|
||||||
this.setState({workingValue: this.props.threshold.value, valid: true})
|
this.setState({workingValue: this.props.threshold.value, valid: true})
|
||||||
|
this.props.onSortColors()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyUp = e => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.thresholdInputRef.blur()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -87,6 +94,8 @@ class Threshold extends Component {
|
||||||
type="number"
|
type="number"
|
||||||
onChange={this.handleChangeWorkingValue}
|
onChange={this.handleChangeWorkingValue}
|
||||||
onBlur={this.handleBlur}
|
onBlur={this.handleBlur}
|
||||||
|
onKeyUp={this.handleKeyUp}
|
||||||
|
ref={r => (this.thresholdInputRef = r)}
|
||||||
/>
|
/>
|
||||||
<ColorDropdown
|
<ColorDropdown
|
||||||
colors={GAUGE_COLORS}
|
colors={GAUGE_COLORS}
|
||||||
|
@ -117,6 +126,7 @@ Threshold.propTypes = {
|
||||||
onDeleteThreshold: func.isRequired,
|
onDeleteThreshold: func.isRequired,
|
||||||
isMin: bool,
|
isMin: bool,
|
||||||
isMax: bool,
|
isMax: bool,
|
||||||
|
onSortColors: func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Threshold
|
export default Threshold
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {Component, PropTypes} from 'react'
|
import React, {Component, PropTypes} from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import TemplateVariableTable from 'src/dashboards/components/template_variables/Table'
|
import TemplateVariableTable from 'src/dashboards/components/template_variables/Table'
|
||||||
|
|
||||||
|
@ -64,15 +64,9 @@ class TemplateVariableManagerWrapper extends Component {
|
||||||
rows: this.props.templates,
|
rows: this.props.templates,
|
||||||
isEdited: false,
|
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 {rows} = this.state
|
||||||
|
|
||||||
const newRow = {
|
const newRow = {
|
||||||
|
@ -95,7 +89,7 @@ class TemplateVariableManagerWrapper extends Component {
|
||||||
this.setState({rows: newRows})
|
this.setState({rows: newRows})
|
||||||
}
|
}
|
||||||
|
|
||||||
onRunQuerySuccess(template, queryConfig, parsedData, tempVar) {
|
onRunQuerySuccess = (template, queryConfig, parsedData, tempVar) => {
|
||||||
const {rows} = this.state
|
const {rows} = this.state
|
||||||
const {id, links} = template
|
const {id, links} = template
|
||||||
const {
|
const {
|
||||||
|
@ -156,7 +150,7 @@ class TemplateVariableManagerWrapper extends Component {
|
||||||
this.setState({rows: newRows, isEdited: true})
|
this.setState({rows: newRows, isEdited: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
onSaveTemplatesSuccess() {
|
onSaveTemplatesSuccess = () => {
|
||||||
const {rows} = this.state
|
const {rows} = this.state
|
||||||
|
|
||||||
const newRows = rows.map(row => ({...row, isNew: false}))
|
const newRows = rows.map(row => ({...row, isNew: false}))
|
||||||
|
@ -164,7 +158,7 @@ class TemplateVariableManagerWrapper extends Component {
|
||||||
this.setState({rows: newRows, isEdited: false})
|
this.setState({rows: newRows, isEdited: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
onDeleteTemplateVariable(templateID) {
|
onDeleteTemplateVariable = templateID => {
|
||||||
const {rows} = this.state
|
const {rows} = this.state
|
||||||
|
|
||||||
const newRows = rows.filter(({id}) => id !== templateID)
|
const newRows = rows.filter(({id}) => id !== templateID)
|
||||||
|
@ -172,7 +166,7 @@ class TemplateVariableManagerWrapper extends Component {
|
||||||
this.setState({rows: newRows, isEdited: true})
|
this.setState({rows: newRows, isEdited: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
tempVarAlreadyExists(testTempVar, testID) {
|
tempVarAlreadyExists = (testTempVar, testID) => {
|
||||||
const {rows: tempVars} = this.state
|
const {rows: tempVars} = this.state
|
||||||
return tempVars.some(
|
return tempVars.some(
|
||||||
({tempVar, id}) => tempVar === testTempVar && id !== testID
|
({tempVar, id}) => tempVar === testTempVar && id !== testID
|
||||||
|
|
|
@ -119,8 +119,6 @@ class RowWrapper extends Component {
|
||||||
selectedTagKey: query && query.tagKey,
|
selectedTagKey: query && query.tagKey,
|
||||||
autoFocusTarget: 'tempVar',
|
autoFocusTarget: 'tempVar',
|
||||||
}
|
}
|
||||||
|
|
||||||
this.runTemplateVariableQuery = ::this.runTemplateVariableQuery
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit = ({
|
handleSubmit = ({
|
||||||
|
|
|
@ -111,7 +111,7 @@ export const DEFAULT_SINGLESTAT_COLORS = [
|
||||||
hex: GAUGE_COLORS[11].hex,
|
hex: GAUGE_COLORS[11].hex,
|
||||||
id: SINGLE_STAT_BASE,
|
id: SINGLE_STAT_BASE,
|
||||||
name: GAUGE_COLORS[11].name,
|
name: GAUGE_COLORS[11].name,
|
||||||
value: 0,
|
value: -999999999999999999,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ import {
|
||||||
templateControlBarVisibilityToggled as templateControlBarVisibilityToggledAction,
|
templateControlBarVisibilityToggled as templateControlBarVisibilityToggledAction,
|
||||||
} from 'shared/actions/app'
|
} from 'shared/actions/app'
|
||||||
import {presentationButtonDispatcher} from 'shared/dispatchers'
|
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 FORMAT_INFLUXQL = 'influxql'
|
||||||
const defaultTimeRange = {
|
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
|
let templatesIncludingDashTime
|
||||||
if (dashboard) {
|
if (dashboard) {
|
||||||
templatesIncludingDashTime = [
|
templatesIncludingDashTime = [
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import _ from 'lodash'
|
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')
|
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'
|
import {getQueryConfig} from 'shared/apis'
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {SFC} from 'react'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
|
||||||
const CustomCell = ({data, columnName}) => {
|
export interface CustomCellProps {
|
||||||
|
data?: string
|
||||||
|
columnName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomCell: SFC<CustomCellProps> = ({data, columnName}) => {
|
||||||
if (columnName === 'time') {
|
if (columnName === 'time') {
|
||||||
const date = moment(new Date(data)).format('MM/DD/YY hh:mm:ssA')
|
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
|
export default CustomCell
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
import {withRouter} from 'react-router'
|
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'
|
import Dropdown from 'shared/components/Dropdown'
|
||||||
|
|
||||||
|
|
|
@ -10,11 +10,6 @@ class QueryEditor extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
value: this.props.query,
|
value: this.props.query,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleKeyDown = ::this.handleKeyDown
|
|
||||||
this.handleChange = ::this.handleChange
|
|
||||||
this.handleUpdate = ::this.handleUpdate
|
|
||||||
this.handleChooseMetaQuery = ::this.handleChooseMetaQuery
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
|
@ -23,7 +18,7 @@ class QueryEditor extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyDown(e) {
|
handleKeyDown = e => {
|
||||||
const {value} = this.state
|
const {value} = this.state
|
||||||
|
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
|
@ -37,15 +32,15 @@ class QueryEditor extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange() {
|
handleChange = () => {
|
||||||
this.setState({value: this.editor.value})
|
this.setState({value: this.editor.value})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUpdate() {
|
handleUpdate = () => {
|
||||||
this.props.onUpdate(this.state.value)
|
this.props.onUpdate(this.state.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChooseMetaQuery(template) {
|
handleChooseMetaQuery = template => {
|
||||||
this.setState({value: template.query})
|
this.setState({value: template.query})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {Table, Column, Cell} from 'fixed-data-table'
|
||||||
import Dropdown from 'shared/components/Dropdown'
|
import Dropdown from 'shared/components/Dropdown'
|
||||||
import CustomCell from 'src/data_explorer/components/CustomCell'
|
import CustomCell from 'src/data_explorer/components/CustomCell'
|
||||||
import TabItem from 'src/data_explorer/components/TableTabItem'
|
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'
|
import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import _ from 'lodash'
|
||||||
import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
||||||
import {resultsToCSV} from 'src/shared/parsing/resultsToCSV.js'
|
import {resultsToCSV} from 'src/shared/parsing/resultsToCSV.js'
|
||||||
import download from 'src/external/download.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 () => {
|
const getCSV = (query, errorThrown) => async () => {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -81,3 +81,16 @@ export const QUERY_TEMPLATES = [
|
||||||
{text: 'Show Stats', query: 'SHOW STATS'},
|
{text: 'Show Stats', query: 'SHOW STATS'},
|
||||||
{text: 'Show Diagnostics', query: 'SHOW DIAGNOSTICS'},
|
{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 _ from 'lodash'
|
||||||
|
|
||||||
import QueryMaker from 'src/data_explorer/components/QueryMaker'
|
import QueryMaker from '../components/QueryMaker'
|
||||||
import Visualization from 'src/data_explorer/components/Visualization'
|
import Visualization from '../components/Visualization'
|
||||||
import WriteDataForm from 'src/data_explorer/components/WriteDataForm'
|
import WriteDataForm from 'src/data_explorer/components/WriteDataForm'
|
||||||
import Header from 'src/data_explorer/containers/Header'
|
import Header from '../containers/Header'
|
||||||
import ResizeContainer from 'src/shared/components/ResizeContainer'
|
import ResizeContainer from 'shared/components/ResizeContainer'
|
||||||
import OverlayTechnologies from 'src/shared/components/OverlayTechnologies'
|
import OverlayTechnologies from 'shared/components/OverlayTechnologies'
|
||||||
import ManualRefresh from 'src/shared/components/ManualRefresh'
|
import ManualRefresh from 'src/shared/components/ManualRefresh'
|
||||||
|
|
||||||
import {VIS_VIEWS, AUTO_GROUP_BY, TEMPLATES} from 'src/shared/constants'
|
import {VIS_VIEWS, AUTO_GROUP_BY} from 'shared/constants'
|
||||||
import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants'
|
import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS, TEMPLATES} from '../constants'
|
||||||
import {errorThrown} from 'shared/actions/errors'
|
import {errorThrown} from 'shared/actions/errors'
|
||||||
import {setAutoRefresh} from 'shared/actions/app'
|
import {setAutoRefresh} from 'shared/actions/app'
|
||||||
import * as dataExplorerActionCreators from 'src/data_explorer/actions/view'
|
import * as dataExplorerActionCreators from 'src/data_explorer/actions/view'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[
|
const groupByTimes = [
|
||||||
{defaultTimeBound: ':interval:', seconds: 604800, menuOption: 'auto'},
|
{defaultTimeBound: ':interval:', seconds: 604800, menuOption: 'auto'},
|
||||||
{defaultTimeBound: 'now() - 5m', seconds: 10, menuOption: '10s'},
|
{defaultTimeBound: 'now() - 5m', seconds: 10, menuOption: '10s'},
|
||||||
{defaultTimeBound: 'now() - 15m', seconds: 60, menuOption: '1m'},
|
{defaultTimeBound: 'now() - 15m', seconds: 60, menuOption: '1m'},
|
||||||
|
@ -10,3 +10,5 @@
|
||||||
{defaultTimeBound: 'now() - 7d', seconds: 86400, menuOption: '1d'},
|
{defaultTimeBound: 'now() - 7d', seconds: 86400, menuOption: '1d'},
|
||||||
{defaultTimeBound: 'now() - 30d', seconds: 604800, menuOption: '7d'},
|
{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')
|
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 FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||||
import ManualRefresh from 'src/shared/components/ManualRefresh'
|
import ManualRefresh from 'src/shared/components/ManualRefresh'
|
||||||
|
|
||||||
import timeRanges from 'hson!shared/data/timeRanges.hson'
|
import {timeRanges} from 'shared/data/timeRanges'
|
||||||
import {
|
import {
|
||||||
getLayouts,
|
getLayouts,
|
||||||
getAppsForHosts,
|
getAppsForHosts,
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
|
<head>
|
||||||
<title>Chronograf</title>
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||||
</head>
|
<title>Chronograf</title>
|
||||||
<body>
|
</head>
|
||||||
<div id='react-root' data-basepath=""></div>
|
|
||||||
</body>
|
<body>
|
||||||
|
<div id='react-root' data-basepath=""></div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1,4 +1,4 @@
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
import {getActiveKapacitor} from 'shared/apis'
|
import {getActiveKapacitor} from 'shared/apis'
|
||||||
import {publishNotification} from 'shared/actions/notifications'
|
import {publishNotification} from 'shared/actions/notifications'
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
const HandlerTabs = ({
|
const HandlerTabs = ({
|
||||||
handlersOnThisAlert,
|
handlersOnThisAlert,
|
||||||
|
|
|
@ -5,8 +5,15 @@ import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||||
|
|
||||||
class KapacitorForm extends Component {
|
class KapacitorForm extends Component {
|
||||||
render() {
|
render() {
|
||||||
const {onInputChange, onReset, kapacitor, onSubmit, exists} = this.props
|
const {
|
||||||
const {url: kapaUrl, name, username, password} = kapacitor
|
onInputChange,
|
||||||
|
onChangeUrl,
|
||||||
|
onReset,
|
||||||
|
kapacitor,
|
||||||
|
onSubmit,
|
||||||
|
exists,
|
||||||
|
} = this.props
|
||||||
|
const {url, name, username, password} = kapacitor
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<div className="page-header">
|
<div className="page-header">
|
||||||
|
@ -35,9 +42,9 @@ class KapacitorForm extends Component {
|
||||||
className="form-control"
|
className="form-control"
|
||||||
id="kapaUrl"
|
id="kapaUrl"
|
||||||
name="kapaUrl"
|
name="kapaUrl"
|
||||||
placeholder={kapaUrl}
|
placeholder={url}
|
||||||
value={kapaUrl}
|
value={url}
|
||||||
onChange={onInputChange}
|
onChange={onChangeUrl}
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -145,6 +152,7 @@ const {func, shape, string, bool} = PropTypes
|
||||||
KapacitorForm.propTypes = {
|
KapacitorForm.propTypes = {
|
||||||
onSubmit: func.isRequired,
|
onSubmit: func.isRequired,
|
||||||
onInputChange: func.isRequired,
|
onInputChange: func.isRequired,
|
||||||
|
onChangeUrl: func.isRequired,
|
||||||
onReset: func.isRequired,
|
onReset: func.isRequired,
|
||||||
kapacitor: shape({
|
kapacitor: shape({
|
||||||
url: string.isRequired,
|
url: string.isRequired,
|
||||||
|
|
|
@ -9,7 +9,7 @@ import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||||
|
|
||||||
import {createRule, editRule} from 'src/kapacitor/apis'
|
import {createRule, editRule} from 'src/kapacitor/apis'
|
||||||
import buildInfluxQLQuery from 'utils/influxql'
|
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'
|
import {DEFAULT_RULE_ID} from 'src/kapacitor/constants'
|
||||||
|
|
||||||
class KapacitorRule extends Component {
|
class KapacitorRule extends Component {
|
||||||
|
|
|
@ -60,6 +60,10 @@ class KapacitorPage extends Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleChangeUrl = ({value}) => {
|
||||||
|
this.setState({kapacitor: {...this.state.kapacitor, url: value}})
|
||||||
|
}
|
||||||
|
|
||||||
handleSubmit = e => {
|
handleSubmit = e => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const {
|
const {
|
||||||
|
@ -143,6 +147,7 @@ class KapacitorPage extends Component {
|
||||||
<KapacitorForm
|
<KapacitorForm
|
||||||
onSubmit={this.handleSubmit}
|
onSubmit={this.handleSubmit}
|
||||||
onInputChange={this.handleInputChange}
|
onInputChange={this.handleInputChange}
|
||||||
|
onChangeUrl={this.handleChangeUrl}
|
||||||
onReset={this.handleResetToDefaults}
|
onReset={this.handleResetToDefaults}
|
||||||
kapacitor={kapacitor}
|
kapacitor={kapacitor}
|
||||||
source={source}
|
source={source}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {PropTypes, Component} from 'react'
|
import React, {PropTypes, Component} from 'react'
|
||||||
import {connect} from 'react-redux'
|
import {connect} from 'react-redux'
|
||||||
import {bindActionCreators} from 'redux'
|
import {bindActionCreators} from 'redux'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import Tickscript from 'src/kapacitor/components/Tickscript'
|
import Tickscript from 'src/kapacitor/components/Tickscript'
|
||||||
import * as kapactiorActionCreators from 'src/kapacitor/actions/view'
|
import * as kapactiorActionCreators from 'src/kapacitor/actions/view'
|
||||||
|
|
|
@ -222,14 +222,14 @@ AnnotationSpan.propTypes = {
|
||||||
annotation: schema.annotation.isRequired,
|
annotation: schema.annotation.isRequired,
|
||||||
mode: string.isRequired,
|
mode: string.isRequired,
|
||||||
dygraph: shape({}).isRequired,
|
dygraph: shape({}).isRequired,
|
||||||
staticLegendHeight: number.isRequired,
|
staticLegendHeight: number,
|
||||||
updateAnnotationAsync: func.isRequired,
|
updateAnnotationAsync: func.isRequired,
|
||||||
updateAnnotation: func.isRequired,
|
updateAnnotation: func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
const mdtp = {
|
const mapDispatchToProps = {
|
||||||
updateAnnotationAsync: actions.updateAnnotationAsync,
|
updateAnnotationAsync: actions.updateAnnotationAsync,
|
||||||
updateAnnotation: actions.updateAnnotation,
|
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 {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
||||||
import {removeUnselectedTemplateValues} from 'src/dashboards/constants'
|
import {removeUnselectedTemplateValues} from 'src/dashboards/constants'
|
||||||
import {intervalValuesPoints} from 'src/shared/constants'
|
|
||||||
|
|
||||||
const AutoRefresh = ComposedComponent => {
|
const AutoRefresh = ComposedComponent => {
|
||||||
class wrapper extends Component {
|
class wrapper extends Component {
|
||||||
|
@ -97,38 +96,31 @@ const AutoRefresh = ComposedComponent => {
|
||||||
const timeSeriesPromises = queries.map(query => {
|
const timeSeriesPromises = queries.map(query => {
|
||||||
const {host, database, rp} = query
|
const {host, database, rp} = query
|
||||||
|
|
||||||
const templatesWithIntervalVals = templates.map(temp => {
|
const templatesWithResolution = templates.map(temp => {
|
||||||
if (temp.tempVar === ':interval:') {
|
if (temp.tempVar === ':interval:') {
|
||||||
if (resolution) {
|
if (resolution) {
|
||||||
// resize event
|
|
||||||
return {
|
return {
|
||||||
...temp,
|
...temp,
|
||||||
values: temp.values.map(v => {
|
values: temp.values.map(
|
||||||
if (v.type === 'resolution') {
|
v => (temp.type === 'resolution' ? {...v, resolution} : v)
|
||||||
return {...v, value: `${resolution}`}
|
),
|
||||||
}
|
|
||||||
if (v.type === 'points') {
|
|
||||||
return {
|
|
||||||
...v,
|
|
||||||
value: `${_.toInteger(Number(resolution) / 3)}`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...temp,
|
...temp,
|
||||||
values: intervalValuesPoints,
|
values: [
|
||||||
|
...temp.values,
|
||||||
|
{value: '1000', type: 'resolution', selected: true},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return temp
|
return temp
|
||||||
})
|
})
|
||||||
|
|
||||||
const tempVars = removeUnselectedTemplateValues(
|
const tempVars = removeUnselectedTemplateValues(templatesWithResolution)
|
||||||
templatesWithIntervalVals
|
|
||||||
)
|
|
||||||
return fetchTimeSeriesAsync(
|
return fetchTimeSeriesAsync(
|
||||||
{
|
{
|
||||||
source: host,
|
source: host,
|
||||||
|
@ -206,6 +198,10 @@ const AutoRefresh = ComposedComponent => {
|
||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wrapper.defaultProps = {
|
||||||
|
inView: true,
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
array,
|
array,
|
||||||
arrayOf,
|
arrayOf,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, {PropTypes, Component} from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||||
|
|
||||||
import autoRefreshItems from 'hson!shared/data/autoRefreshes.hson'
|
import autoRefreshItems from 'shared/data/autoRefreshes'
|
||||||
|
|
||||||
class AutoRefreshDropdown extends Component {
|
class AutoRefreshDropdown extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, {PropTypes, Component} from 'react'
|
||||||
import rome from 'rome'
|
import rome from 'rome'
|
||||||
import moment from 'moment'
|
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'
|
const dateFormat = 'YYYY-MM-DD HH:mm'
|
||||||
|
|
||||||
class CustomTimeRange extends Component {
|
class CustomTimeRange extends Component {
|
||||||
|
|
|
@ -12,20 +12,17 @@ class CustomTimeRangeDropdown extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
expanded: false,
|
expanded: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleToggleDropdown = ::this.handleToggleDropdown
|
|
||||||
this.handleCloseDropdown = ::this.handleCloseDropdown
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClickOutside() {
|
handleClickOutside() {
|
||||||
this.handleCloseDropdown()
|
this.handleCloseDropdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
handleToggleDropdown() {
|
handleToggleDropdown = () => {
|
||||||
this.setState({expanded: !this.state.expanded})
|
this.setState({expanded: !this.state.expanded})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCloseDropdown() {
|
handleCloseDropdown = () => {
|
||||||
this.setState({expanded: false})
|
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 Dropdown from 'shared/components/Dropdown'
|
||||||
|
|
||||||
import {showDatabases} from 'shared/apis/metaQuery'
|
import {showDatabases} from 'shared/apis/metaQuery'
|
||||||
|
|
|
@ -30,15 +30,13 @@ class DeleteConfirmButtons extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
isConfirming: false,
|
isConfirming: false,
|
||||||
}
|
}
|
||||||
this.handleClickDelete = ::this.handleClickDelete
|
|
||||||
this.handleCancel = ::this.handleCancel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClickDelete() {
|
handleClickDelete = () => {
|
||||||
this.setState({isConfirming: true})
|
this.setState({isConfirming: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancel() {
|
handleCancel = () => {
|
||||||
this.setState({isConfirming: false})
|
this.setState({isConfirming: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import React, {Component, PropTypes} from 'react'
|
import React, {Component} from 'react'
|
||||||
import {Link} from 'react-router'
|
import PropTypes from 'prop-types'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
import DropdownMenu, {DropdownMenuEmpty} from 'shared/components/DropdownMenu'
|
||||||
import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index'
|
import DropdownInput from 'shared/components/DropdownInput'
|
||||||
|
import DropdownHead from 'shared/components/DropdownHead'
|
||||||
|
|
||||||
class Dropdown extends Component {
|
export class Dropdown extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -36,6 +37,7 @@ class Dropdown extends Component {
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toggleMenu(e)
|
this.toggleMenu(e)
|
||||||
if (this.props.onClick) {
|
if (this.props.onClick) {
|
||||||
this.props.onClick(e)
|
this.props.onClick(e)
|
||||||
|
@ -56,6 +58,7 @@ class Dropdown extends Component {
|
||||||
if (e) {
|
if (e) {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.state.isOpen) {
|
if (!this.state.isOpen) {
|
||||||
this.setState({
|
this.setState({
|
||||||
searchTerm: '',
|
searchTerm: '',
|
||||||
|
@ -63,6 +66,7 @@ class Dropdown extends Component {
|
||||||
highlightedItemIndex: null,
|
highlightedItemIndex: null,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({isOpen: !this.state.isOpen})
|
this.setState({isOpen: !this.state.isOpen})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,17 +99,17 @@ class Dropdown extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFilterChange = e => {
|
handleFilterChange = e => {
|
||||||
if (e.target.value === null || e.target.value === '') {
|
if (e.target.value) {
|
||||||
this.setState({
|
return this.setState({searchTerm: e.target.value}, () =>
|
||||||
searchTerm: '',
|
|
||||||
filteredItems: this.props.items,
|
|
||||||
highlightedItemIndex: null,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.setState({searchTerm: e.target.value}, () =>
|
|
||||||
this.applyFilter(this.state.searchTerm)
|
this.applyFilter(this.state.searchTerm)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
searchTerm: '',
|
||||||
|
filteredItems: this.props.items,
|
||||||
|
highlightedItemIndex: null,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
applyFilter = searchTerm => {
|
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() {
|
render() {
|
||||||
const {
|
const {
|
||||||
items,
|
items,
|
||||||
|
addNew,
|
||||||
|
actions,
|
||||||
selected,
|
selected,
|
||||||
|
disabled,
|
||||||
|
iconName,
|
||||||
|
tabIndex,
|
||||||
className,
|
className,
|
||||||
menuClass,
|
menuClass,
|
||||||
iconName,
|
menuWidth,
|
||||||
|
menuLabel,
|
||||||
buttonSize,
|
buttonSize,
|
||||||
buttonColor,
|
buttonColor,
|
||||||
toggleStyle,
|
toggleStyle,
|
||||||
useAutoComplete,
|
useAutoComplete,
|
||||||
disabled,
|
|
||||||
tabIndex,
|
|
||||||
} = this.props
|
} = 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
|
const disabledClass = disabled ? 'disabled' : null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
|
@ -232,48 +157,46 @@ class Dropdown extends Component {
|
||||||
})}
|
})}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
ref={r => (this.dropdownRef = r)}
|
ref={r => (this.dropdownRef = r)}
|
||||||
|
data-test="dropdown-toggle"
|
||||||
>
|
>
|
||||||
{useAutoComplete && isOpen
|
{useAutoComplete && isOpen
|
||||||
? <div
|
? <DropdownInput
|
||||||
className={`dropdown-autocomplete dropdown-toggle ${buttonSize} ${buttonColor} ${disabledClass}`}
|
searchTerm={searchTerm}
|
||||||
style={toggleStyle}
|
buttonSize={buttonSize}
|
||||||
>
|
buttonColor={buttonColor}
|
||||||
<input
|
toggleStyle={toggleStyle}
|
||||||
ref="dropdownAutoComplete"
|
disabledClass={disabledClass}
|
||||||
className="dropdown-autocomplete--input"
|
onFilterChange={this.handleFilterChange}
|
||||||
type="text"
|
onFilterKeyPress={this.handleFilterKeyPress}
|
||||||
autoFocus={true}
|
/>
|
||||||
placeholder="Filter items..."
|
: <DropdownHead
|
||||||
spellCheck={false}
|
iconName={iconName}
|
||||||
onChange={this.handleFilterChange}
|
selected={selected}
|
||||||
onKeyDown={this.handleFilterKeyPress}
|
searchTerm={searchTerm}
|
||||||
value={searchTerm}
|
buttonSize={buttonSize}
|
||||||
/>
|
buttonColor={buttonColor}
|
||||||
<span className="caret" />
|
toggleStyle={toggleStyle}
|
||||||
</div>
|
disabledClass={disabledClass}
|
||||||
: <div
|
/>}
|
||||||
className={`btn dropdown-toggle ${buttonSize} ${buttonColor} ${disabledClass}`}
|
{isOpen && menuItems.length
|
||||||
style={toggleStyle}
|
? <DropdownMenu
|
||||||
>
|
addNew={addNew}
|
||||||
{iconName
|
actions={actions}
|
||||||
? <span className={classnames('icon', {[iconName]: true})} />
|
items={menuItems}
|
||||||
: null}
|
selected={selected}
|
||||||
<span className="dropdown-selected">
|
menuClass={menuClass}
|
||||||
{selected}
|
menuWidth={menuWidth}
|
||||||
</span>
|
menuLabel={menuLabel}
|
||||||
<span className="caret" />
|
onAction={this.handleAction}
|
||||||
</div>}
|
useAutoComplete={useAutoComplete}
|
||||||
{isOpen && menuItems.length ? this.renderMenu() : null}
|
onSelection={this.handleSelection}
|
||||||
{isOpen && !menuItems.length
|
onHighlight={this.handleHighlight}
|
||||||
? <ul
|
highlightedItemIndex={highlightedItemIndex}
|
||||||
className={classnames('dropdown-menu', {
|
/>
|
||||||
'dropdown-menu--no-highlight': useAutoComplete,
|
: <DropdownMenuEmpty
|
||||||
[menuClass]: menuClass,
|
useAutoComplete={useAutoComplete}
|
||||||
})}
|
menuClass={menuClass}
|
||||||
>
|
/>}
|
||||||
<li className="dropdown-empty">No matching items</li>
|
|
||||||
</ul>
|
|
||||||
: null}
|
|
||||||
</div>
|
</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() {
|
render() {
|
||||||
const {isHidden, staticLegendHeight} = this.state
|
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'}
|
let dygraphStyle = {...this.props.containerStyle, zIndex: '2'}
|
||||||
if (staticLegend) {
|
if (staticLegend) {
|
||||||
const cellVerticalPadding = 16
|
const cellVerticalPadding = 16
|
||||||
|
@ -339,17 +340,19 @@ class Dygraph extends Component {
|
||||||
/>
|
/>
|
||||||
{staticLegend &&
|
{staticLegend &&
|
||||||
<StaticLegend
|
<StaticLegend
|
||||||
|
dygraphSeries={this.hashColorDygraphSeries()}
|
||||||
dygraph={this.dygraph}
|
dygraph={this.dygraph}
|
||||||
handleReceiveStaticLegendHeight={
|
handleReceiveStaticLegendHeight={
|
||||||
this.handleReceiveStaticLegendHeight
|
this.handleReceiveStaticLegendHeight
|
||||||
}
|
}
|
||||||
/>}
|
/>}
|
||||||
|
{nestedGraph && React.cloneElement(nestedGraph, {staticLegendHeight})}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {array, arrayOf, bool, func, shape, string} = PropTypes
|
const {array, arrayOf, bool, func, node, shape, string} = PropTypes
|
||||||
|
|
||||||
Dygraph.defaultProps = {
|
Dygraph.defaultProps = {
|
||||||
axes: {
|
axes: {
|
||||||
|
@ -408,6 +411,7 @@ Dygraph.propTypes = {
|
||||||
dygraphRef: func,
|
dygraphRef: func,
|
||||||
onZoom: func,
|
onZoom: func,
|
||||||
mode: string,
|
mode: string,
|
||||||
|
children: node,
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = ({annotations: {mode}}) => ({
|
const mapStateToProps = ({annotations: {mode}}) => ({
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {PropTypes, Component} from 'react'
|
import React, {PropTypes, Component} from 'react'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import {makeLegendStyles, removeMeasurement} from 'shared/graphs/helpers'
|
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 classnames from 'classnames'
|
||||||
import {Scrollbars} from 'react-custom-scrollbars'
|
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 {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 {
|
class FillQuery extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
|
|
@ -10,10 +10,6 @@ class FunctionSelector extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
localSelectedItems: this.props.selectedItems,
|
localSelectedItems: this.props.selectedItems,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onSelect = ::this.onSelect
|
|
||||||
this.onSingleSelect = ::this.onSingleSelect
|
|
||||||
this.handleApplyFunctions = ::this.handleApplyFunctions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUpdate(nextProps) {
|
componentWillUpdate(nextProps) {
|
||||||
|
@ -22,7 +18,7 @@ class FunctionSelector extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelect(item, e) {
|
onSelect = (item, e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
const {localSelectedItems} = this.state
|
const {localSelectedItems} = this.state
|
||||||
|
@ -37,7 +33,7 @@ class FunctionSelector extends Component {
|
||||||
this.setState({localSelectedItems: nextItems})
|
this.setState({localSelectedItems: nextItems})
|
||||||
}
|
}
|
||||||
|
|
||||||
onSingleSelect(item) {
|
onSingleSelect = item => {
|
||||||
if (item === this.state.localSelectedItems[0]) {
|
if (item === this.state.localSelectedItems[0]) {
|
||||||
this.props.onApply([])
|
this.props.onApply([])
|
||||||
this.setState({localSelectedItems: []})
|
this.setState({localSelectedItems: []})
|
||||||
|
@ -47,11 +43,11 @@ class FunctionSelector extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isSelected(item) {
|
isSelected = item => {
|
||||||
return !!this.state.localSelectedItems.find(text => text === item)
|
return !!this.state.localSelectedItems.find(text => text === item)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleApplyFunctions(e) {
|
handleApplyFunctions = e => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
this.props.onApply(this.state.localSelectedItems)
|
this.props.onApply(this.state.localSelectedItems)
|
||||||
|
|
|
@ -93,13 +93,8 @@ class LineGraph extends Component {
|
||||||
top: '8px',
|
top: '8px',
|
||||||
}
|
}
|
||||||
|
|
||||||
let prefix
|
const prefix = axes ? axes.y.prefix : ''
|
||||||
let suffix
|
const suffix = axes ? axes.y.suffix : ''
|
||||||
|
|
||||||
if (axes) {
|
|
||||||
prefix = axes.y.prefix
|
|
||||||
suffix = axes.y.suffix
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dygraph graph--hasYLabel" style={{height: '100%'}}>
|
<div className="dygraph graph--hasYLabel" style={{height: '100%'}}>
|
||||||
|
@ -123,17 +118,17 @@ class LineGraph extends Component {
|
||||||
containerStyle={containerStyle}
|
containerStyle={containerStyle}
|
||||||
staticLegend={staticLegend}
|
staticLegend={staticLegend}
|
||||||
isGraphFilled={showSingleStat ? false : isGraphFilled}
|
isGraphFilled={showSingleStat ? false : isGraphFilled}
|
||||||
/>
|
>
|
||||||
{showSingleStat
|
{showSingleStat &&
|
||||||
? <SingleStat
|
<SingleStat
|
||||||
prefix={prefix}
|
prefix={prefix}
|
||||||
suffix={suffix}
|
suffix={suffix}
|
||||||
data={data}
|
data={data}
|
||||||
lineGraph={true}
|
lineGraph={true}
|
||||||
colors={colors}
|
colors={colors}
|
||||||
cellHeight={cellHeight}
|
cellHeight={cellHeight}
|
||||||
/>
|
/>}
|
||||||
: null}
|
</Dygraph>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {Component, PropTypes} from 'react'
|
import React, {Component, PropTypes} from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import {connect} from 'react-redux'
|
import {connect} from 'react-redux'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||||
import AnnotationWindow from 'shared/components/AnnotationWindow'
|
import AnnotationWindow from 'shared/components/AnnotationWindow'
|
||||||
|
|
|
@ -15,9 +15,6 @@ import {
|
||||||
class Notifications extends Component {
|
class Notifications extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.renderNotification = ::this.renderNotification
|
|
||||||
this.renderDismiss = ::this.renderDismiss
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
|
@ -26,7 +23,7 @@ class Notifications extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNotification(type, message) {
|
renderNotification = (type, message) => {
|
||||||
const isDismissed = this.props.dismissedNotifications[
|
const isDismissed = this.props.dismissedNotifications[
|
||||||
getNotificationID(message, type)
|
getNotificationID(message, type)
|
||||||
]
|
]
|
||||||
|
@ -48,7 +45,7 @@ class Notifications extends Component {
|
||||||
|
|
||||||
handleDismiss = type => () => this.props.dismissNotification(type)
|
handleDismiss = type => () => this.props.dismissNotification(type)
|
||||||
|
|
||||||
renderDismiss(type) {
|
renderDismiss = type => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className="close"
|
className="close"
|
||||||
|
|
|
@ -1,21 +1,17 @@
|
||||||
import React from 'react'
|
import React, {Component} from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
|
|
||||||
export default function enhanceWithClickOutside(WrappedComponent) {
|
export default function enhanceWithClickOutside(WrappedComponent) {
|
||||||
const componentName = WrappedComponent.displayName || WrappedComponent.name
|
return class extends Component {
|
||||||
|
|
||||||
return React.createClass({
|
|
||||||
displayName: `Wrapped${componentName}`,
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
document.addEventListener('click', this.handleClickOutside, true)
|
document.addEventListener('click', this.handleClickOutside, true)
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
document.removeEventListener('click', this.handleClickOutside, true)
|
document.removeEventListener('click', this.handleClickOutside, true)
|
||||||
},
|
}
|
||||||
|
|
||||||
handleClickOutside(e) {
|
handleClickOutside = e => {
|
||||||
const domNode = ReactDOM.findDOMNode(this)
|
const domNode = ReactDOM.findDOMNode(this)
|
||||||
if (
|
if (
|
||||||
(!domNode || !domNode.contains(e.target)) &&
|
(!domNode || !domNode.contains(e.target)) &&
|
||||||
|
@ -23,7 +19,7 @@ export default function enhanceWithClickOutside(WrappedComponent) {
|
||||||
) {
|
) {
|
||||||
this.wrappedComponent.handleClickOutside(e)
|
this.wrappedComponent.handleClickOutside(e)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
@ -32,6 +28,6 @@ export default function enhanceWithClickOutside(WrappedComponent) {
|
||||||
ref={ref => (this.wrappedComponent = ref)}
|
ref={ref => (this.wrappedComponent = ref)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {Component, PropTypes} from 'react'
|
import React, {Component, PropTypes} from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import ClickOutsideInput from 'shared/components/ClickOutsideInput'
|
import ClickOutsideInput from 'shared/components/ClickOutsideInput'
|
||||||
|
|
||||||
|
|
|
@ -17,11 +17,6 @@ class ResizeContainer extends Component {
|
||||||
topHeight: props.initialTopHeight,
|
topHeight: props.initialTopHeight,
|
||||||
bottomHeight: props.initialBottomHeight,
|
bottomHeight: props.initialBottomHeight,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleStartDrag = ::this.handleStartDrag
|
|
||||||
this.handleStopDrag = ::this.handleStopDrag
|
|
||||||
this.handleMouseLeave = ::this.handleMouseLeave
|
|
||||||
this.handleDrag = ::this.handleDrag
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -38,19 +33,19 @@ class ResizeContainer extends Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleStartDrag() {
|
handleStartDrag = () => {
|
||||||
this.setState({isDragging: true})
|
this.setState({isDragging: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleStopDrag() {
|
handleStopDrag = () => {
|
||||||
this.setState({isDragging: false})
|
this.setState({isDragging: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseLeave() {
|
handleMouseLeave = () => {
|
||||||
this.setState({isDragging: false})
|
this.setState({isDragging: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDrag(e) {
|
handleDrag = e => {
|
||||||
if (!this.state.isDragging) {
|
if (!this.state.isDragging) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
import {connect} from 'react-redux'
|
import {connect} from 'react-redux'
|
||||||
|
|
||||||
import ReactTooltip from 'react-tooltip'
|
import ReactTooltip from 'react-tooltip'
|
||||||
|
|
|
@ -3,6 +3,7 @@ import classnames from 'classnames'
|
||||||
import lastValues from 'shared/parsing/lastValues'
|
import lastValues from 'shared/parsing/lastValues'
|
||||||
|
|
||||||
import {SMALL_CELL_HEIGHT} from 'shared/graphs/helpers'
|
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 {SINGLE_STAT_TEXT} from 'src/dashboards/constants/gaugeColors'
|
||||||
import {generateSingleStatHexs} from 'shared/constants/colorOperations'
|
import {generateSingleStatHexs} from 'shared/constants/colorOperations'
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ class SingleStat extends PureComponent {
|
||||||
prefix,
|
prefix,
|
||||||
suffix,
|
suffix,
|
||||||
lineGraph,
|
lineGraph,
|
||||||
|
staticLegendHeight,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
// If data for this graph is being fetched for the first time, show a graph-wide spinner.
|
// 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
|
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 (
|
return (
|
||||||
<div
|
<div className="single-stat" style={singleStatStyles}>
|
||||||
className="single-stat"
|
|
||||||
style={{backgroundColor: bgColor, color: textColor}}
|
|
||||||
>
|
|
||||||
<span
|
<span
|
||||||
className={classnames('single-stat--value', {
|
className={classnames('single-stat--value', {
|
||||||
'single-stat--small': cellHeight === SMALL_CELL_HEIGHT,
|
'single-stat--small': cellHeight === SMALL_CELL_HEIGHT,
|
||||||
|
@ -77,6 +92,7 @@ SingleStat.propTypes = {
|
||||||
prefix: string,
|
prefix: string,
|
||||||
suffix: string,
|
suffix: string,
|
||||||
lineGraph: bool,
|
lineGraph: bool,
|
||||||
|
staticLegendHeight: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SingleStat
|
export default SingleStat
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import ReactTooltip from 'react-tooltip'
|
import ReactTooltip from 'react-tooltip'
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {PropTypes, Component} from 'react'
|
import React, {PropTypes, Component} from 'react'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
import {removeMeasurement} from 'shared/graphs/helpers'
|
import {removeMeasurement} from 'shared/graphs/helpers'
|
||||||
|
|
||||||
const staticLegendItemClassname = (visibilities, i, hoverEnabled) => {
|
const staticLegendItemClassname = (visibilities, i, hoverEnabled) => {
|
||||||
|
@ -65,13 +65,10 @@ class StaticLegend extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {dygraph} = this.props
|
const {dygraphSeries} = this.props
|
||||||
const {visibilities} = this.state
|
const {visibilities} = this.state
|
||||||
|
const labels = _.keys(dygraphSeries)
|
||||||
const labels = dygraph ? _.drop(dygraph.getLabels()) : []
|
const colors = _.map(labels, l => dygraphSeries[l].color)
|
||||||
const colors = dygraph
|
|
||||||
? _.map(labels, l => dygraph.attributes_.series_[l].options.color)
|
|
||||||
: []
|
|
||||||
|
|
||||||
const hoverEnabled = labels.length > 1
|
const hoverEnabled = labels.length > 1
|
||||||
|
|
||||||
|
@ -105,7 +102,7 @@ class StaticLegend extends Component {
|
||||||
const {shape, func} = PropTypes
|
const {shape, func} = PropTypes
|
||||||
|
|
||||||
StaticLegend.propTypes = {
|
StaticLegend.propTypes = {
|
||||||
sharedLegend: shape({}),
|
dygraphSeries: shape({}),
|
||||||
dygraph: shape({}),
|
dygraph: shape({}),
|
||||||
handleReceiveStaticLegendHeight: func.isRequired,
|
handleReceiveStaticLegendHeight: func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {Component, PropTypes} from 'react'
|
import React, {Component, PropTypes} from 'react'
|
||||||
|
|
||||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
class TagsAddButton extends Component {
|
class TagsAddButton extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import OnClickOutside from 'shared/components/OnClickOutside'
|
||||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||||
import CustomTimeRangeOverlay from 'shared/components/CustomTimeRangeOverlay'
|
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'
|
import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index'
|
||||||
|
|
||||||
const dateFormat = 'YYYY-MM-DD HH:mm'
|
const dateFormat = 'YYYY-MM-DD HH:mm'
|
||||||
|
|
|
@ -430,20 +430,6 @@ export const DEFAULT_SOURCE = {
|
||||||
metaUrl: '',
|
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 =>
|
export const IS_STATIC_LEGEND = legend =>
|
||||||
_.get(legend, 'type', false) === 'static'
|
_.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
|
return INVALID
|
||||||
}
|
}
|
||||||
|
|
||||||
const isUpperValid = moment(upper).isValid()
|
const isUpperValid = moment(new Date(upper)).isValid()
|
||||||
const isLowerValid = moment(lower).isValid()
|
const isLowerValid = moment(new Date(lower)).isValid()
|
||||||
|
|
||||||
// {lower: <Date>, upper: <Date>}
|
// {lower: <Date>, upper: <Date>}
|
||||||
if (isLowerValid && isUpperValid) {
|
if (isLowerValid && isUpperValid) {
|
||||||
|
@ -103,8 +103,8 @@ const getMomentUnit = unit => {
|
||||||
|
|
||||||
export const shiftDate = (date, quantity, unit) => {
|
export const shiftDate = (date, quantity, unit) => {
|
||||||
if (!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 {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'
|
import * as actionTypes from 'src/status/constants/actionTypes'
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,9 @@
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.dygraph-child > .single-stat {
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
.single-stat--value {
|
.single-stat--value {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {buildQuery} from 'utils/influxql'
|
import {buildQuery} from 'utils/influxql'
|
||||||
import {TYPE_SHIFTED, TYPE_QUERY_CONFIG} from 'src/dashboards/constants'
|
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 buildCannedDashboardQuery = (query, {lower, upper}, host) => {
|
||||||
const {defaultGroupBy} = timeRanges.find(range => range.lower === lower) || {
|
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'
|
import {NULL_STRING} from 'shared/constants/queryFillOptions'
|
||||||
|
|
||||||
|
|
|
@ -157,9 +157,9 @@ function _buildGroupByTime(groupBy) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return ` GROUP BY time(${groupBy.time === AUTO_GROUP_BY
|
return ` GROUP BY ${groupBy.time === AUTO_GROUP_BY
|
||||||
? TEMP_VAR_INTERVAL
|
? TEMP_VAR_INTERVAL
|
||||||
: `${groupBy.time}`})`
|
: `time(${groupBy.time})`}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function _buildGroupByTags(groupBy) {
|
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