diff --git a/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.html b/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.html new file mode 100644 index 0000000000000..4c8afa31f64e9 --- /dev/null +++ b/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.html @@ -0,0 +1,287 @@ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+
+ + + Ranges + +
+
+ +
+
+ + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+

+ + Required: You must specify at least one range. +

+
+ +
+ Add Range +
+
Note: colors can be changed in the legend
+
+
+
+
+
+
+ + + Color Options + +
+
+
+
+
+ +
+ +
+
reset colors
+
+ +
+ +
+ +
+
+
+
+
+ + + + + +
+
+
+ + + Style + +
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+
+
diff --git a/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.js b/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.js new file mode 100644 index 0000000000000..6523c57d64433 --- /dev/null +++ b/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.js @@ -0,0 +1,93 @@ +import { uiModules } from 'ui/modules'; +import gaugeOptionsTemplate from 'plugins/kbn_vislib_vis_types/controls/gauge_options.html'; +import _ from 'lodash'; +const module = uiModules.get('kibana'); + +module.directive('gaugeOptions', function () { + return { + restrict: 'E', + template: gaugeOptionsTemplate, + replace: true, + link: function ($scope) { + + $scope.showColorRange = true; + + $scope.$watch('vis.params.gauge.gaugeType', type => { + switch (type) { + case 'Arc': + $scope.vis.params.gauge.type = 'meter'; + $scope.vis.params.gauge.minAngle = undefined; + $scope.vis.params.gauge.maxAngle = undefined; + break; + case 'Circle': + $scope.vis.params.gauge.type = 'meter'; + $scope.vis.params.gauge.minAngle = 0; + $scope.vis.params.gauge.maxAngle = 2 * Math.PI; + break; + case 'Metric': + $scope.vis.params.gauge.type = 'simple'; + } + }); + + + const updateLegend = () => { + if (!$scope.vis.params.gauge.style.bgColor && !$scope.vis.params.gauge.style.labelColor) { + $scope.vis.params.addLegend = false; + } else { + $scope.vis.params.addLegend = true; + } + }; + + $scope.$watch('vis.params.gauge.gaugeColorMode', newValue => { + switch (newValue) { + case 'Labels': + $scope.vis.params.gauge.style.labelColor = true; + $scope.vis.params.gauge.style.bgColor = false; + break; + case 'Background': + $scope.vis.params.gauge.style.labelColor = false; + $scope.vis.params.gauge.style.bgColor = true; + break; + case 'None': + $scope.vis.params.gauge.style.labelColor = false; + $scope.vis.params.gauge.style.bgColor = false; + break; + } + updateLegend(); + }); + + $scope.resetColors = function () { + $scope.uiState.set('vis.colors', null); + $scope.customColors = false; + }; + + $scope.getGreaterThan = function (index) { + if (index === 0) return 0; + return $scope.vis.params.gauge.colorsRange[index - 1].to; + }; + + $scope.addRange = function () { + const previousRange = _.last($scope.vis.params.gauge.colorsRange); + const from = previousRange ? previousRange.to : 0; + const to = previousRange ? from + (previousRange.to - previousRange.from) : 100; + $scope.vis.params.gauge.colorsRange.push({ from: from, to: to }); + }; + + $scope.removeRange = function (index) { + $scope.vis.params.gauge.colorsRange.splice(index, 1); + }; + + $scope.getColor = function (index) { + const defaultColors = this.uiState.get('vis.defaultColors'); + const overwriteColors = this.uiState.get('vis.colors'); + const colors = defaultColors ? _.defaults({}, overwriteColors, defaultColors) : overwriteColors; + return colors ? Object.values(colors)[index] : 'transparent'; + }; + + $scope.uiState.on('colorChanged', () => { + $scope.customColors = true; + }); + + } + }; +}); diff --git a/src/core_plugins/kbn_vislib_vis_types/public/editors/gauge.html b/src/core_plugins/kbn_vislib_vis_types/public/editors/gauge.html new file mode 100644 index 0000000000000..9ec797371648d --- /dev/null +++ b/src/core_plugins/kbn_vislib_vis_types/public/editors/gauge.html @@ -0,0 +1,2 @@ + + diff --git a/src/core_plugins/kbn_vislib_vis_types/public/gauge.js b/src/core_plugins/kbn_vislib_vis_types/public/gauge.js new file mode 100644 index 0000000000000..b878d2b4c7be1 --- /dev/null +++ b/src/core_plugins/kbn_vislib_vis_types/public/gauge.js @@ -0,0 +1,95 @@ +import { VisVisTypeProvider } from 'ui/vis/vis_type'; +import { VislibVisTypeVislibVisTypeProvider } from 'ui/vislib_vis_type/vislib_vis_type'; +import { VisSchemasProvider } from 'ui/vis/schemas'; +import gaugeTemplate from 'plugins/kbn_vislib_vis_types/editors/gauge.html'; +import { vislibColorMaps } from 'ui/vislib/components/color/colormaps'; +import image from './images/icon-gauge.svg'; + +export default function GaugeVisType(Private) { + const VisType = Private(VisVisTypeProvider); + const VislibVisType = Private(VislibVisTypeVislibVisTypeProvider); + const Schemas = Private(VisSchemasProvider); + + return new VislibVisType({ + name: 'gauge', + title: 'Gauge', + image, + description: `Gauges indicate the status of a metric. Use it to show how a metric's value relates + to reference threshold values.`, + category: VisType.CATEGORY.DATA, + params: { + defaults: { + addTooltip: true, + addLegend: true, + + gauge: { + verticalSplit: false, + extendRange: true, + percentageMode: false, + gaugeType: 'Arc', + gaugeStyle: 'Full', + backStyle: 'Full', + orientation: 'vertical', + colorSchema: 'Green to Red', + gaugeColorMode: 'Labels', + colorsRange: [ + { from: 0, to: 50 }, + { from: 50, to: 75 }, + { from: 75, to: 100 } + ], + invertColors: false, + labels: { + show: true, + color: 'black' + }, + scale: { + show: true, + labels: false, + color: '#333', + }, + type: 'meter', + style: { + bgWidth: 0.9, + width: 0.9, + mask: false, + bgMask: false, + maskBars: 50, + bgFill: '#eee', + bgColor: true, + subText: '', + fontSize: 60, + } + } + }, + gaugeTypes: ['Arc', 'Circle', 'Metric'], + gaugeColorMode: ['None', 'Labels', 'Background'], + gaugeStyles: ['Full', 'Bars', 'Lines'], + scales: ['linear', 'log', 'square root'], + colorSchemas: Object.keys(vislibColorMaps), + editor: gaugeTemplate + }, + implementsRenderComplete: true, + schemas: new Schemas([ + { + group: 'metrics', + name: 'metric', + title: 'Metric', + min: 1, + aggFilter: [ + '!std_dev', '!geo_centroid', '!percentiles', '!percentile_ranks', + '!derivative', '!serial_diff', '!moving_avg', '!cumulative_sum'], + defaults: [ + { schema: 'metric', type: 'count' } + ] + }, + { + group: 'buckets', + name: 'group', + title: 'Split Group', + min: 0, + max: 1, + aggFilter: '!geohash_grid' + } + ]) + }); +} diff --git a/src/core_plugins/kbn_vislib_vis_types/public/goal.js b/src/core_plugins/kbn_vislib_vis_types/public/goal.js new file mode 100644 index 0000000000000..170907d4e7489 --- /dev/null +++ b/src/core_plugins/kbn_vislib_vis_types/public/goal.js @@ -0,0 +1,89 @@ +import { VisVisTypeProvider } from 'ui/vis/vis_type'; +import { VislibVisTypeVislibVisTypeProvider } from 'ui/vislib_vis_type/vislib_vis_type'; +import { VisSchemasProvider } from 'ui/vis/schemas'; +import gaugeTemplate from 'plugins/kbn_vislib_vis_types/editors/gauge.html'; +import { vislibColorMaps } from 'ui/vislib/components/color/colormaps'; +import image from './images/icon-goal.svg'; + +export default function GoalVisType(Private) { + const VisType = Private(VisVisTypeProvider); + const VislibVisType = Private(VislibVisTypeVislibVisTypeProvider); + const Schemas = Private(VisSchemasProvider); + + return new VislibVisType({ + name: 'goal', + title: 'Goal', + image, + description: 'A goal chart indicates how close you are to your final goal.', + category: VisType.CATEGORY.DATA, + params: { + defaults: { + addTooltip: true, + addLegend: false, + type: 'gauge', + gauge: { + verticalSplit: false, + autoExtend: false, + percentageMode: true, + gaugeType: 'Arc', + gaugeStyle: 'Full', + backStyle: 'Full', + orientation: 'vertical', + useRanges: false, + colorSchema: 'Green to Red', + gaugeColorMode: 'None', + colorsRange: [ + { from: 0, to: 10000 } + ], + invertColors: false, + labels: { + show: true, + color: 'black' + }, + scale: { + show: false, + labels: false, + color: '#333', + width: 2 + }, + type: 'meter', + style: { + bgFill: '#000', + bgColor: false, + labelColor: false, + subText: '', + fontSize: 60, + } + } + }, + gaugeTypes: ['Arc', 'Circle', 'Metric'], + gaugeColorMode: ['None', 'Labels', 'Background'], + scales: ['linear', 'log', 'square root'], + colorSchemas: Object.keys(vislibColorMaps), + editor: gaugeTemplate + }, + implementsRenderComplete: true, + schemas: new Schemas([ + { + group: 'metrics', + name: 'metric', + title: 'Metric', + min: 1, + aggFilter: [ + '!std_dev', '!geo_centroid', '!percentiles', '!percentile_ranks', + '!derivative', '!serial_diff', '!moving_avg', '!cumulative_sum'], + defaults: [ + { schema: 'metric', type: 'count' } + ] + }, + { + group: 'buckets', + name: 'group', + title: 'Split Group', + min: 0, + max: 1, + aggFilter: '!geohash_grid' + } + ]) + }); +} diff --git a/src/core_plugins/kbn_vislib_vis_types/public/images/icon-gauge.svg b/src/core_plugins/kbn_vislib_vis_types/public/images/icon-gauge.svg new file mode 100644 index 0000000000000..8bb387312a26f --- /dev/null +++ b/src/core_plugins/kbn_vislib_vis_types/public/images/icon-gauge.svg @@ -0,0 +1,19 @@ + + + + icon-gauge + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/core_plugins/kbn_vislib_vis_types/public/images/icon-goal.svg b/src/core_plugins/kbn_vislib_vis_types/public/images/icon-goal.svg new file mode 100644 index 0000000000000..6274d8e891798 --- /dev/null +++ b/src/core_plugins/kbn_vislib_vis_types/public/images/icon-goal.svg @@ -0,0 +1,27 @@ + + + + icon goal + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/core_plugins/metric_vis/public/images/icon-number.svg b/src/core_plugins/kbn_vislib_vis_types/public/images/icon-number.svg similarity index 100% rename from src/core_plugins/metric_vis/public/images/icon-number.svg rename to src/core_plugins/kbn_vislib_vis_types/public/images/icon-number.svg diff --git a/src/core_plugins/kbn_vislib_vis_types/public/kbn_vislib_vis_types.js b/src/core_plugins/kbn_vislib_vis_types/public/kbn_vislib_vis_types.js index b779fbf537074..671806d4afadc 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/kbn_vislib_vis_types.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/kbn_vislib_vis_types.js @@ -7,6 +7,9 @@ import areaVisTypeProvider from 'plugins/kbn_vislib_vis_types/area'; import tileMapVisTypeProvider from 'plugins/kbn_vislib_vis_types/tile_map'; import heatmapVisTypeProvider from 'plugins/kbn_vislib_vis_types/heatmap'; import horizontalBarVisTypeProvider from 'plugins/kbn_vislib_vis_types/horizontal_bar'; +import gaugeVisTypeProvider from 'plugins/kbn_vislib_vis_types/gauge'; +import goalVisTypeProvider from 'plugins/kbn_vislib_vis_types/goal'; +import metricVisTypeProvider from 'plugins/kbn_vislib_vis_types/metric'; VisTypesRegistryProvider.register(histogramVisTypeProvider); VisTypesRegistryProvider.register(lineVisTypeProvider); @@ -15,3 +18,6 @@ VisTypesRegistryProvider.register(areaVisTypeProvider); VisTypesRegistryProvider.register(tileMapVisTypeProvider); VisTypesRegistryProvider.register(heatmapVisTypeProvider); VisTypesRegistryProvider.register(horizontalBarVisTypeProvider); +VisTypesRegistryProvider.register(gaugeVisTypeProvider); +VisTypesRegistryProvider.register(goalVisTypeProvider); +VisTypesRegistryProvider.register(metricVisTypeProvider); diff --git a/src/core_plugins/kbn_vislib_vis_types/public/metric.js b/src/core_plugins/kbn_vislib_vis_types/public/metric.js new file mode 100644 index 0000000000000..b9087d45848e3 --- /dev/null +++ b/src/core_plugins/kbn_vislib_vis_types/public/metric.js @@ -0,0 +1,89 @@ +import { VisVisTypeProvider } from 'ui/vis/vis_type'; +import { VislibVisTypeVislibVisTypeProvider } from 'ui/vislib_vis_type/vislib_vis_type'; +import { VisSchemasProvider } from 'ui/vis/schemas'; +import gaugeTemplate from 'plugins/kbn_vislib_vis_types/editors/gauge.html'; +import { vislibColorMaps } from 'ui/vislib/components/color/colormaps'; +import image from './images/icon-number.svg'; + +export default function MetricVisType(Private) { + const VisType = Private(VisVisTypeProvider); + const VislibVisType = Private(VislibVisTypeVislibVisTypeProvider); + const Schemas = Private(VisSchemasProvider); + + return new VislibVisType({ + name: 'metric', + title: 'Metric', + image, + description: 'Display a calculation as a single number', + category: VisType.CATEGORY.DATA, + params: { + defaults: { + addTooltip: true, + addLegend: false, + type: 'gauge', + gauge: { + verticalSplit: false, + autoExtend: false, + percentageMode: false, + gaugeType: 'Metric', + gaugeStyle: 'Full', + backStyle: 'Full', + orientation: 'vertical', + colorSchema: 'Green to Red', + gaugeColorMode: 'None', + useRange: false, + colorsRange: [ + { from: 0, to: 100 }, + ], + invertColors: false, + labels: { + show: true, + color: 'black' + }, + scale: { + show: false, + labels: false, + color: '#333', + width: 2 + }, + type: 'simple', + style: { + fontSize: 60, + bgFill: '#000', + bgColor: false, + labelColor: false, + subText: '' + } + } + }, + gaugeTypes: ['Arc', 'Circle', 'Metric'], + gaugeColorMode: ['None', 'Labels', 'Background'], + scales: ['linear', 'log', 'square root'], + colorSchemas: Object.keys(vislibColorMaps), + editor: gaugeTemplate + }, + implementsRenderComplete: true, + schemas: new Schemas([ + { + group: 'metrics', + name: 'metric', + title: 'Metric', + min: 1, + aggFilter: [ + '!std_dev', '!geo_centroid', '!percentiles', '!percentile_ranks', + '!derivative', '!serial_diff', '!moving_avg', '!cumulative_sum'], + defaults: [ + { schema: 'metric', type: 'count' } + ] + }, + { + group: 'buckets', + name: 'group', + title: 'Split Group', + min: 0, + max: 1, + aggFilter: '!geohash_grid' + } + ]) + }); +} diff --git a/src/core_plugins/kibana/public/visualize/editor/styles/_editor.less b/src/core_plugins/kibana/public/visualize/editor/styles/_editor.less index 6a02387d231e5..6006e89484576 100644 --- a/src/core_plugins/kibana/public/visualize/editor/styles/_editor.less +++ b/src/core_plugins/kibana/public/visualize/editor/styles/_editor.less @@ -28,6 +28,10 @@ .form-horizontal .control-label { text-align: left; } + + .kuiSideBarSelect { + width: 100%; + } } nesting-indicator { diff --git a/src/core_plugins/metric_vis/index.js b/src/core_plugins/metric_vis/index.js deleted file mode 100644 index cba768a6aaf26..0000000000000 --- a/src/core_plugins/metric_vis/index.js +++ /dev/null @@ -1,13 +0,0 @@ -export default function (kibana) { - - return new kibana.Plugin({ - - uiExports: { - visTypes: [ - 'plugins/metric_vis/metric_vis' - ] - } - - }); - -} diff --git a/src/core_plugins/metric_vis/package.json b/src/core_plugins/metric_vis/package.json deleted file mode 100644 index e570261fc30dd..0000000000000 --- a/src/core_plugins/metric_vis/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "metric_vis", - "version": "kibana" -} diff --git a/src/core_plugins/metric_vis/public/__tests__/metric_vis.js b/src/core_plugins/metric_vis/public/__tests__/metric_vis.js deleted file mode 100644 index ce24438777efa..0000000000000 --- a/src/core_plugins/metric_vis/public/__tests__/metric_vis.js +++ /dev/null @@ -1,60 +0,0 @@ -import $ from 'jquery'; -import ngMock from 'ng_mock'; -import expect from 'expect.js'; - -import { VisProvider } from 'ui/vis'; -import LogstashIndexPatternStubProvider from 'fixtures/stubbed_logstash_index_pattern'; -import MetricVisProvider from '../metric_vis'; - -describe('metric_vis', () => { - let setup = null; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject((Private, $rootScope) => { - setup = () => { - const Vis = Private(VisProvider); - const metricVisType = Private(MetricVisProvider); - const indexPattern = Private(LogstashIndexPatternStubProvider); - - indexPattern.stubSetFieldFormat('ip', 'url', { - urlTemplate: 'http://ip.info?address={{value}}', - labelTemplate: 'ip[{{value}}]' - }); - - const vis = new Vis(indexPattern, { - type: 'metric', - aggs: [{ id: '1', type: 'top_hits', schema: 'metric', params: { field: 'ip' } }], - }); - - const $el = $('
'); - const renderbot = metricVisType.createRenderbot(vis, $el); - const render = (esResponse) => { - renderbot.render(esResponse); - $rootScope.$digest(); - }; - - return { $el, render }; - }; - })); - - it('renders html value from field formatter', () => { - const { $el, render } = setup(); - - const ip = '235.195.237.208'; - render({ - hits: { total: 0, hits: [] }, - aggregations: { - '1': { - hits: { total: 1, hits: [{ _source: { ip } }] } - } - } - }); - - const $link = $el - .find('a[href]') - .filter(function () { return this.href.includes('ip.info'); }); - - expect($link).to.have.length(1); - expect($link.text()).to.be(`ip[${ip}]`); - }); -}); diff --git a/src/core_plugins/metric_vis/public/__tests__/metric_vis_controller.js b/src/core_plugins/metric_vis/public/__tests__/metric_vis_controller.js deleted file mode 100644 index c07b3cfc4bd33..0000000000000 --- a/src/core_plugins/metric_vis/public/__tests__/metric_vis_controller.js +++ /dev/null @@ -1,50 +0,0 @@ -import ngMock from 'ng_mock'; -import expect from 'expect.js'; -import $ from 'jquery'; - -describe('metric vis', function () { - let $scope; - - const formatter = function (value) { - return value.toFixed(3); - }; - - beforeEach(ngMock.module('kibana/metric_vis')); - beforeEach(ngMock.inject(function ($rootScope, $controller) { - $scope = $rootScope.$new(); - const $element = $('
'); - $controller('KbnMetricVisController', { $scope, $element }); - $scope.$digest(); - })); - - it('should set the metric label and value', function () { - $scope.processTableGroups({ - tables: [{ - columns: [{ title: 'Count' }], - rows: [[ { toString: () => formatter(4301021) } ]] - }] - }); - - expect($scope.metrics.length).to.be(1); - expect($scope.metrics[0].label).to.be('Count'); - expect($scope.metrics[0].value).to.be('4301021.000'); - }); - - it('should support multi-value metrics', function () { - $scope.processTableGroups({ - tables: [{ - columns: [ - { title: '1st percentile of bytes' }, - { title: '99th percentile of bytes' } - ], - rows: [[ { toString: () => formatter(182) }, { toString: () => formatter(445842.4634666484) } ]] - }] - }); - - expect($scope.metrics.length).to.be(2); - expect($scope.metrics[0].label).to.be('1st percentile of bytes'); - expect($scope.metrics[0].value).to.be('182.000'); - expect($scope.metrics[1].label).to.be('99th percentile of bytes'); - expect($scope.metrics[1].value).to.be('445842.463'); - }); -}); diff --git a/src/core_plugins/metric_vis/public/metric_vis.html b/src/core_plugins/metric_vis/public/metric_vis.html deleted file mode 100644 index 42bc974a73ec5..0000000000000 --- a/src/core_plugins/metric_vis/public/metric_vis.html +++ /dev/null @@ -1,6 +0,0 @@ -
-
-
-
{{metric.label}}
-
-
diff --git a/src/core_plugins/metric_vis/public/metric_vis.js b/src/core_plugins/metric_vis/public/metric_vis.js deleted file mode 100644 index ce67b4d4dd1dc..0000000000000 --- a/src/core_plugins/metric_vis/public/metric_vis.js +++ /dev/null @@ -1,55 +0,0 @@ -import 'plugins/metric_vis/metric_vis.less'; -import 'plugins/metric_vis/metric_vis_controller'; -import { VisVisTypeProvider } from 'ui/vis/vis_type'; -import { TemplateVisTypeProvider } from 'ui/template_vis_type/template_vis_type'; -import { VisSchemasProvider } from 'ui/vis/schemas'; -import metricVisTemplate from 'plugins/metric_vis/metric_vis.html'; -import metricVisParamsTemplate from 'plugins/metric_vis/metric_vis_params.html'; -import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; -import image from './images/icon-number.svg'; -// we need to load the css ourselves - -// we also need to load the controller and used by the template - -// register the provider with the visTypes registry -VisTypesRegistryProvider.register(MetricVisProvider); - -function MetricVisProvider(Private) { - const VisType = Private(VisVisTypeProvider); - const TemplateVisType = Private(TemplateVisTypeProvider); - const Schemas = Private(VisSchemasProvider); - - // return the visType object, which kibana will use to display and configure new - // Vis object of this type. - return new TemplateVisType({ - name: 'metric', - title: 'Metric', - image, - description: 'Display a calculation as a single number', - category: VisType.CATEGORY.DATA, - template: metricVisTemplate, - params: { - defaults: { - handleNoResults: true, - fontSize: 60 - }, - editor: metricVisParamsTemplate - }, - implementsRenderComplete: true, - schemas: new Schemas([ - { - group: 'metrics', - name: 'metric', - title: 'Metric', - min: 1, - aggFilter: ['!derivative', '!geo_centroid'], - defaults: [ - { type: 'count', schema: 'metric' } - ] - } - ]) - }); -} - -// export the provider so that the visType can be required with Private() -export default MetricVisProvider; diff --git a/src/core_plugins/metric_vis/public/metric_vis.less b/src/core_plugins/metric_vis/public/metric_vis.less deleted file mode 100644 index a770c09ec428e..0000000000000 --- a/src/core_plugins/metric_vis/public/metric_vis.less +++ /dev/null @@ -1,18 +0,0 @@ -@import (reference) "~ui/styles/mixins.less"; - -.metric-vis { - width: 100%; - display: flex; - flex-direction: row; - flex-wrap: wrap; - - .metric-value { - font-weight: bold; - .ellipsis(); - } - - .metric-container { - text-align: center; - margin: auto; - } -} diff --git a/src/core_plugins/metric_vis/public/metric_vis_controller.js b/src/core_plugins/metric_vis/public/metric_vis_controller.js deleted file mode 100644 index 2f98c3f9ae20f..0000000000000 --- a/src/core_plugins/metric_vis/public/metric_vis_controller.js +++ /dev/null @@ -1,36 +0,0 @@ -import { AggResponseTabifyProvider } from 'ui/agg_response/tabify/tabify'; -import { uiModules } from 'ui/modules'; -// get the kibana/metric_vis module, and make sure that it requires the "kibana" module if it -// didn't already -const module = uiModules.get('kibana/metric_vis', ['kibana']); - -module.controller('KbnMetricVisController', function ($scope, $element, Private) { - const tabifyAggResponse = Private(AggResponseTabifyProvider); - - const metrics = $scope.metrics = []; - - $scope.processTableGroups = function (tableGroups) { - tableGroups.tables.forEach(function (table) { - table.columns.forEach(function (column, i) { - const value = table.rows[0][i]; - - metrics.push({ - label: column.title, - value: value.toString('html') - }); - }); - }); - }; - - $scope.$watch('esResponse', function (resp) { - if (resp) { - const options = { - asAggConfigResults: true - }; - - metrics.length = 0; - $scope.processTableGroups(tabifyAggResponse($scope.vis, resp, options)); - $element.trigger('renderComplete'); - } - }); -}); diff --git a/src/core_plugins/metric_vis/public/metric_vis_params.html b/src/core_plugins/metric_vis/public/metric_vis_params.html deleted file mode 100644 index cdbfc7f2d3998..0000000000000 --- a/src/core_plugins/metric_vis/public/metric_vis_params.html +++ /dev/null @@ -1,4 +0,0 @@ -
- - -
\ No newline at end of file diff --git a/src/fixtures/vislib/mock_data/terms/_seriesMultiple.js b/src/fixtures/vislib/mock_data/terms/_seriesMultiple.js new file mode 100644 index 0000000000000..64b9d0e452403 --- /dev/null +++ b/src/fixtures/vislib/mock_data/terms/_seriesMultiple.js @@ -0,0 +1,71 @@ +import _ from 'lodash'; + +module.exports = { + 'yAxisLabel': 'Count', + 'zAxisLabel': 'machine.os.raw: Descending', + 'yScale': null, + 'series': [{ + 'label': 'ios', + 'aggLabel': 'Count', + 'aggId': '1', + 'values': [{ + 'x': '_all', + 'y': 2820, + 'series': 'ios' + }] + }, { + 'label': 'win 7', + 'aggLabel': 'Count', + 'aggId': '1', + 'values': [{ + 'x': '_all', + 'y': 2319, + 'series': 'win 7' + }] + }, { + 'label': 'win 8', + 'aggLabel': 'Count', + 'aggId': '1', + 'values': [{ + 'x': '_all', + 'y': 1835, + 'series': 'win 8' + }] + }, { + 'label': 'win xp', + 'aggLabel': 'Count', + 'aggId': '1', + 'values': [{ + 'x': '_all', + 'y': 734, + 'series': 'win xp' + }] + }, { + 'label': 'osx', + 'aggLabel': 'Count', + 'aggId': '1', + 'values': [{ + 'x': '_all', + 'y': 1352, + 'series': 'osx' + }] + }], + 'hits': 14005, + 'xAxisFormatter': function (val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } + else if (val == null) { + return ''; + } + else { + return '' + val; + } + }, + 'yAxisFormatter': function (val) { + return val; + }, + 'tooltipFormatter': function (d) { + return d; + } +}; diff --git a/src/ui/public/styles/dark-theme.less b/src/ui/public/styles/dark-theme.less index 46c9652a508fa..eac3b45a08beb 100644 --- a/src/ui/public/styles/dark-theme.less +++ b/src/ui/public/styles/dark-theme.less @@ -270,6 +270,10 @@ fill: @svg-tick-text-color; } + .chart-label { + fill: @svg-tick-text-color; + } + .brush .extent { stroke: @svg-brush-color; } diff --git a/src/ui/public/vislib/__tests__/visualizations/gauge_chart.js b/src/ui/public/vislib/__tests__/visualizations/gauge_chart.js new file mode 100644 index 0000000000000..70e39e0adf9fa --- /dev/null +++ b/src/ui/public/vislib/__tests__/visualizations/gauge_chart.js @@ -0,0 +1,144 @@ +import expect from 'expect.js'; +import ngMock from 'ng_mock'; +import $ from 'jquery'; +import _ from 'lodash'; +import data from 'fixtures/vislib/mock_data/terms/_seriesMultiple'; +import FixturesVislibVisFixtureProvider from 'fixtures/vislib/_vis_fixture'; +import 'ui/persisted_state'; + +describe('Vislib Gauge Chart Test Suite', function () { + let PersistedState; + let vislibVis; + let vis; + let persistedState; + let chartEl; + const visLibParams = { + type: 'gauge', + addTooltip: true, + addLegend: false, + gauge: { + verticalSplit: false, + autoExtend: false, + percentageMode: false, + gaugeStyle: 'Full', + backStyle: 'Full', + orientation: 'vertical', + colorSchema: 'Green to Red', + colorsRange: [ + { from: 0, to: 1500 }, + { from: 1500, to: 2500 }, + { from: 2500, to: 3000 } + ], + invertColors: false, + labels: { + show: true, + color: 'black' + }, + scale: { + show: true, + labels: false, + color: '#333', + width: 2 + }, + type: 'meter', + style: { + bgWidth: 0.9, + width: 0.9, + mask: false, + bgMask: false, + maskBars: 50, + bgFill: '#eee', + subText: '', + } + } + }; + + function generateVis(opts = {}) { + const config = _.defaultsDeep({}, opts, visLibParams); + if (vis) { + vis.destroy(); + $('.visualize-chart').remove(); + } + vis = vislibVis(config); + persistedState = new PersistedState(); + vis.on('brush', _.noop); + vis.render(data, persistedState); + chartEl = vis.handler.charts[0].chartEl; + } + + beforeEach(ngMock.module('kibana')); + beforeEach(ngMock.inject(function (Private, $injector) { + vislibVis = Private(FixturesVislibVisFixtureProvider); + PersistedState = $injector.get('PersistedState'); + generateVis(); + })); + + afterEach(function () { + vis.destroy(); + $('.visualize-chart').remove(); + }); + + it('creates meter gauge', function () { + expect($(chartEl).find('svg').length).to.equal(5); + expect($(chartEl).find('svg > g > g > text').text()).to.equal('2820231918357341352'); + }); + + it('creates circle gauge', function () { + generateVis({ + gauge: { + minAngle: 0, + maxAngle: 2 * Math.PI + } + }); + expect($(chartEl).find('svg').length).to.equal(5); + }); + + it('creates gauge with percentage mode', function () { + generateVis({ + gauge: { + percentageMode: true + } + }); + expect($(chartEl).find('svg > g > g > text').text()).to.equal('94%77%61%24%45%'); + }); + + it('creates gauge with vertical mode', function () { + generateVis({ + gauge: { + verticalSplit: true + } + }); + expect($(chartEl).find('svg').width()).to.equal($(chartEl).width()); + }); + + it('applies range settings correctly', function () { + const paths = $(chartEl).find('svg > g > g:nth-child(1) > path:nth-child(2)'); + const fills = []; + paths.each(function () { fills.push(this.style.fill); }); + expect(fills).to.eql([ + 'rgb(165, 0, 38)', + 'rgb(255, 255, 190)', + 'rgb(255, 255, 190)', + 'rgb(0, 104, 55)', + 'rgb(0, 104, 55)' + ]); + }); + + it('applies color schema correctly', function () { + generateVis({ + gauge: { + colorSchema: 'Blues' + } + }); + const paths = $(chartEl).find('svg > g > g:nth-child(1) > path:nth-child(2)'); + const fills = []; + paths.each(function () { fills.push(this.style.fill); }); + expect(fills).to.eql([ + 'rgb(8, 48, 107)', + 'rgb(107, 174, 214)', + 'rgb(107, 174, 214)', + 'rgb(247, 251, 255)', + 'rgb(247, 251, 255)' + ]); + }); +}); diff --git a/src/ui/public/vislib/components/color/colormaps.js b/src/ui/public/vislib/components/color/colormaps.js index 797a521abb372..cf848ac5e4929 100644 --- a/src/ui/public/vislib/components/color/colormaps.js +++ b/src/ui/public/vislib/components/color/colormaps.js @@ -2562,3 +2562,175 @@ vislibColorMaps['Yellow to Red'] = [[0.000, [1.000, 1.000, 0.800]], [0.998, [0.502, 0.000, 0.149]], [1.000, [0.502, 0.000, 0.149]]]; +vislibColorMaps['Green to Red'] = [ + [0, [0, 0.408, 0.216]], [0.002, [0, 0.408, 0.216]], [0.004, [0.004, 0.415, 0.22]], + [0.006, [0.004, 0.415, 0.22]], [0.008, [0.008, 0.423, 0.223]], [0.01, [0.008, 0.423, 0.223]], + [0.012, [0.012, 0.43, 0.227]], [0.014, [0.012, 0.43, 0.227]], [0.016, [0.016, 0.437, 0.231]], + [0.018, [0.016, 0.437, 0.231]], [0.02, [0.02, 0.445, 0.235]], [0.022, [0.02, 0.445, 0.235]], + [0.023, [0.024, 0.452, 0.239]], [0.025, [0.024, 0.452, 0.239]], [0.027, [0.028, 0.46, 0.243]], + [0.029, [0.028, 0.46, 0.243]], [0.031, [0.032, 0.467, 0.246]], [0.033, [0.032, 0.467, 0.246]], + [0.035, [0.036, 0.474, 0.25]], [0.037, [0.036, 0.474, 0.25]], [0.039, [0.04, 0.482, 0.254]], + [0.041, [0.04, 0.482, 0.254]], [0.043, [0.044, 0.489, 0.258]], [0.045, [0.044, 0.489, 0.258]], + [0.047, [0.048, 0.496, 0.262]], [0.049, [0.048, 0.496, 0.262]], [0.051, [0.052, 0.504, 0.266]], + [0.053, [0.052, 0.504, 0.266]], [0.055, [0.056, 0.511, 0.27]], [0.057, [0.056, 0.511, 0.27]], + [0.059, [0.06, 0.519, 0.273]], [0.061, [0.06, 0.519, 0.273]], [0.063, [0.064, 0.526, 0.277]], + [0.065, [0.064, 0.526, 0.277]], [0.067, [0.068, 0.533, 0.281]], [0.068, [0.068, 0.533, 0.281]], + [0.07, [0.072, 0.541, 0.285]], [0.072, [0.072, 0.541, 0.285]], [0.074, [0.076, 0.548, 0.289]], + [0.076, [0.076, 0.548, 0.289]], [0.078, [0.08, 0.555, 0.293]], [0.08, [0.08, 0.555, 0.293]], + [0.082, [0.084, 0.563, 0.296]], [0.084, [0.084, 0.563, 0.296]], [0.086, [0.088, 0.57, 0.3]], + [0.088, [0.088, 0.57, 0.3]], [0.09, [0.092, 0.578, 0.304]], [0.092, [0.092, 0.578, 0.304]], + [0.094, [0.096, 0.585, 0.308]], [0.096, [0.096, 0.585, 0.308]], [0.098, [0.1, 0.592, 0.312]], + [0.1, [0.1, 0.592, 0.312]], [0.102, [0.108, 0.599, 0.315]], [0.104, [0.108, 0.599, 0.315]], + [0.106, [0.119, 0.605, 0.318]], [0.108, [0.119, 0.605, 0.318]], [0.11, [0.131, 0.61, 0.321]], + [0.112, [0.131, 0.61, 0.321]], [0.114, [0.143, 0.616, 0.324]], [0.115, [0.143, 0.616, 0.324]], + [0.117, [0.155, 0.622, 0.327]], [0.119, [0.155, 0.622, 0.327]], [0.121, [0.166, 0.627, 0.33]], + [0.123, [0.166, 0.627, 0.33]], [0.125, [0.178, 0.633, 0.333]], [0.127, [0.178, 0.633, 0.333]], + [0.129, [0.19, 0.639, 0.336]], [0.131, [0.19, 0.639, 0.336]], [0.133, [0.201, 0.644, 0.339]], + [0.135, [0.201, 0.644, 0.339]], [0.137, [0.213, 0.65, 0.341]], [0.139, [0.213, 0.65, 0.341]], + [0.141, [0.225, 0.656, 0.344]], [0.143, [0.225, 0.656, 0.344]], [0.145, [0.236, 0.662, 0.347]], + [0.147, [0.236, 0.662, 0.347]], [0.149, [0.248, 0.667, 0.35]], [0.151, [0.248, 0.667, 0.35]], + [0.153, [0.26, 0.673, 0.353]], [0.155, [0.26, 0.673, 0.353]], [0.157, [0.271, 0.679, 0.356]], + [0.159, [0.271, 0.679, 0.356]], [0.16, [0.283, 0.684, 0.359]], [0.162, [0.283, 0.684, 0.359]], + [0.164, [0.295, 0.69, 0.362]], [0.166, [0.295, 0.69, 0.362]], [0.168, [0.306, 0.696, 0.365]], + [0.17, [0.306, 0.696, 0.365]], [0.172, [0.318, 0.701, 0.368]], [0.174, [0.318, 0.701, 0.368]], + [0.176, [0.33, 0.707, 0.371]], [0.178, [0.33, 0.707, 0.371]], [0.18, [0.342, 0.713, 0.374]], + [0.182, [0.342, 0.713, 0.374]], [0.184, [0.353, 0.718, 0.377]], [0.186, [0.353, 0.718, 0.377]], + [0.188, [0.365, 0.724, 0.379]], [0.19, [0.365, 0.724, 0.379]], [0.192, [0.377, 0.73, 0.382]], + [0.194, [0.377, 0.73, 0.382]], [0.196, [0.388, 0.735, 0.385]], [0.198, [0.388, 0.735, 0.385]], + [0.2, [0.4, 0.741, 0.388]], [0.202, [0.4, 0.741, 0.388]], [0.204, [0.41, 0.745, 0.389]], + [0.205, [0.41, 0.745, 0.389]], [0.207, [0.42, 0.75, 0.39]], [0.209, [0.42, 0.75, 0.39]], + [0.211, [0.43, 0.754, 0.391]], [0.213, [0.43, 0.754, 0.391]], [0.215, [0.439, 0.758, 0.393]], + [0.217, [0.439, 0.758, 0.393]], [0.219, [0.449, 0.763, 0.394]], [0.221, [0.449, 0.763, 0.394]], + [0.223, [0.459, 0.767, 0.395]], [0.225, [0.459, 0.767, 0.395]], [0.227, [0.469, 0.771, 0.396]], + [0.229, [0.469, 0.771, 0.396]], [0.231, [0.479, 0.776, 0.397]], [0.233, [0.479, 0.776, 0.397]], + [0.235, [0.489, 0.78, 0.398]], [0.237, [0.489, 0.78, 0.398]], [0.239, [0.498, 0.784, 0.399]], + [0.241, [0.498, 0.784, 0.399]], [0.243, [0.508, 0.789, 0.4]], [0.245, [0.508, 0.789, 0.4]], + [0.247, [0.518, 0.793, 0.401]], [0.249, [0.518, 0.793, 0.401]], [0.25, [0.528, 0.797, 0.402]], + [0.252, [0.528, 0.797, 0.402]], [0.254, [0.538, 0.801, 0.403]], [0.256, [0.538, 0.801, 0.403]], + [0.258, [0.548, 0.806, 0.404]], [0.26, [0.548, 0.806, 0.404]], [0.262, [0.557, 0.81, 0.405]], + [0.264, [0.557, 0.81, 0.405]], [0.266, [0.567, 0.814, 0.407]], [0.268, [0.567, 0.814, 0.407]], + [0.27, [0.577, 0.819, 0.408]], [0.272, [0.577, 0.819, 0.408]], [0.274, [0.587, 0.823, 0.409]], + [0.276, [0.587, 0.823, 0.409]], [0.278, [0.597, 0.827, 0.41]], [0.28, [0.597, 0.827, 0.41]], + [0.282, [0.607, 0.832, 0.411]], [0.284, [0.607, 0.832, 0.411]], [0.286, [0.617, 0.836, 0.412]], + [0.288, [0.617, 0.836, 0.412]], [0.29, [0.626, 0.84, 0.413]], [0.292, [0.626, 0.84, 0.413]], + [0.294, [0.636, 0.845, 0.414]], [0.295, [0.636, 0.845, 0.414]], [0.297, [0.646, 0.849, 0.415]], + [0.299, [0.646, 0.849, 0.415]], [0.301, [0.655, 0.853, 0.418]], [0.303, [0.655, 0.853, 0.418]], + [0.305, [0.663, 0.856, 0.423]], [0.307, [0.663, 0.856, 0.423]], [0.309, [0.671, 0.859, 0.428]], + [0.311, [0.671, 0.859, 0.428]], [0.313, [0.678, 0.863, 0.433]], [0.315, [0.678, 0.863, 0.433]], + [0.317, [0.686, 0.866, 0.439]], [0.319, [0.686, 0.866, 0.439]], [0.321, [0.694, 0.87, 0.444]], + [0.323, [0.694, 0.87, 0.444]], [0.325, [0.702, 0.873, 0.449]], [0.327, [0.702, 0.873, 0.449]], + [0.329, [0.71, 0.876, 0.454]], [0.331, [0.71, 0.876, 0.454]], [0.333, [0.718, 0.88, 0.459]], + [0.335, [0.718, 0.88, 0.459]], [0.337, [0.725, 0.883, 0.464]], [0.339, [0.725, 0.883, 0.464]], + [0.341, [0.733, 0.887, 0.469]], [0.342, [0.733, 0.887, 0.469]], [0.344, [0.741, 0.89, 0.474]], + [0.346, [0.741, 0.89, 0.474]], [0.348, [0.749, 0.893, 0.479]], [0.35, [0.749, 0.893, 0.479]], + [0.352, [0.757, 0.897, 0.484]], [0.354, [0.757, 0.897, 0.484]], [0.356, [0.765, 0.9, 0.489]], + [0.358, [0.765, 0.9, 0.489]], [0.36, [0.773, 0.903, 0.494]], [0.362, [0.773, 0.903, 0.494]], + [0.364, [0.78, 0.907, 0.499]], [0.366, [0.78, 0.907, 0.499]], [0.368, [0.788, 0.91, 0.504]], + [0.37, [0.788, 0.91, 0.504]], [0.372, [0.796, 0.914, 0.51]], [0.374, [0.796, 0.914, 0.51]], + [0.376, [0.804, 0.917, 0.515]], [0.378, [0.804, 0.917, 0.515]], [0.38, [0.812, 0.92, 0.52]], + [0.382, [0.812, 0.92, 0.52]], [0.384, [0.82, 0.924, 0.525]], [0.386, [0.82, 0.924, 0.525]], + [0.387, [0.827, 0.927, 0.53]], [0.389, [0.827, 0.927, 0.53]], [0.391, [0.835, 0.93, 0.535]], + [0.393, [0.835, 0.93, 0.535]], [0.395, [0.843, 0.934, 0.54]], [0.397, [0.843, 0.934, 0.54]], + [0.399, [0.851, 0.937, 0.545]], [0.401, [0.851, 0.937, 0.545]], [0.403, [0.857, 0.94, 0.553]], + [0.405, [0.857, 0.94, 0.553]], [0.407, [0.863, 0.942, 0.561]], [0.409, [0.863, 0.942, 0.561]], + [0.411, [0.869, 0.945, 0.569]], [0.413, [0.869, 0.945, 0.569]], [0.415, [0.874, 0.947, 0.577]], + [0.417, [0.874, 0.947, 0.577]], [0.419, [0.88, 0.95, 0.585]], [0.421, [0.88, 0.95, 0.585]], + [0.423, [0.886, 0.952, 0.593]], [0.425, [0.886, 0.952, 0.593]], [0.427, [0.892, 0.954, 0.601]], + [0.429, [0.892, 0.954, 0.601]], [0.431, [0.898, 0.957, 0.609]], [0.432, [0.898, 0.957, 0.609]], + [0.434, [0.904, 0.959, 0.617]], [0.436, [0.904, 0.959, 0.617]], [0.438, [0.909, 0.962, 0.625]], + [0.44, [0.909, 0.962, 0.625]], [0.442, [0.915, 0.964, 0.633]], [0.444, [0.915, 0.964, 0.633]], + [0.446, [0.921, 0.967, 0.641]], [0.448, [0.921, 0.967, 0.641]], [0.45, [0.927, 0.969, 0.649]], + [0.452, [0.927, 0.969, 0.649]], [0.454, [0.933, 0.972, 0.657]], [0.456, [0.933, 0.972, 0.657]], + [0.458, [0.939, 0.974, 0.665]], [0.46, [0.939, 0.974, 0.665]], [0.462, [0.944, 0.977, 0.673]], + [0.464, [0.944, 0.977, 0.673]], [0.466, [0.95, 0.979, 0.681]], [0.468, [0.95, 0.979, 0.681]], + [0.47, [0.956, 0.982, 0.689]], [0.472, [0.956, 0.982, 0.689]], [0.474, [0.962, 0.984, 0.697]], + [0.476, [0.962, 0.984, 0.697]], [0.477, [0.968, 0.986, 0.705]], [0.479, [0.968, 0.986, 0.705]], + [0.481, [0.974, 0.989, 0.713]], [0.483, [0.974, 0.989, 0.713]], [0.485, [0.98, 0.991, 0.721]], + [0.487, [0.98, 0.991, 0.721]], [0.489, [0.985, 0.994, 0.729]], [0.491, [0.985, 0.994, 0.729]], + [0.493, [0.991, 0.996, 0.737]], [0.495, [0.991, 0.996, 0.737]], [0.497, [0.997, 0.999, 0.745]], + [0.499, [0.997, 0.999, 0.745]], [0.501, [1, 0.998, 0.745]], [0.503, [1, 0.998, 0.745]], + [0.505, [1, 0.993, 0.737]], [0.507, [1, 0.993, 0.737]], [0.509, [1, 0.988, 0.729]], + [0.511, [1, 0.988, 0.729]], [0.513, [0.999, 0.983, 0.721]], [0.515, [0.999, 0.983, 0.721]], + [0.517, [0.999, 0.979, 0.713]], [0.519, [0.999, 0.979, 0.713]], [0.521, [0.999, 0.974, 0.705]], + [0.523, [0.999, 0.974, 0.705]], [0.524, [0.999, 0.969, 0.697]], [0.526, [0.999, 0.969, 0.697]], + [0.528, [0.999, 0.964, 0.689]], [0.53, [0.999, 0.964, 0.689]], [0.532, [0.999, 0.959, 0.681]], + [0.534, [0.999, 0.959, 0.681]], [0.536, [0.999, 0.955, 0.673]], [0.538, [0.999, 0.955, 0.673]], + [0.54, [0.998, 0.95, 0.665]], [0.542, [0.998, 0.95, 0.665]], [0.544, [0.998, 0.945, 0.657]], + [0.546, [0.998, 0.945, 0.657]], [0.548, [0.998, 0.94, 0.649]], [0.55, [0.998, 0.94, 0.649]], + [0.552, [0.998, 0.936, 0.641]], [0.554, [0.998, 0.936, 0.641]], [0.556, [0.998, 0.931, 0.633]], + [0.558, [0.998, 0.931, 0.633]], [0.56, [0.998, 0.926, 0.625]], [0.562, [0.998, 0.926, 0.625]], + [0.564, [0.997, 0.921, 0.617]], [0.566, [0.997, 0.921, 0.617]], [0.568, [0.997, 0.917, 0.609]], + [0.569, [0.997, 0.917, 0.609]], [0.571, [0.997, 0.912, 0.601]], [0.573, [0.997, 0.912, 0.601]], + [0.575, [0.997, 0.907, 0.593]], [0.577, [0.997, 0.907, 0.593]], [0.579, [0.997, 0.902, 0.585]], + [0.581, [0.997, 0.902, 0.585]], [0.583, [0.997, 0.898, 0.577]], [0.585, [0.997, 0.898, 0.577]], + [0.587, [0.997, 0.893, 0.569]], [0.589, [0.997, 0.893, 0.569]], [0.591, [0.996, 0.888, 0.561]], + [0.593, [0.996, 0.888, 0.561]], [0.595, [0.996, 0.883, 0.553]], [0.597, [0.996, 0.883, 0.553]], + [0.599, [0.996, 0.878, 0.545]], [0.601, [0.996, 0.878, 0.545]], [0.603, [0.996, 0.871, 0.539]], + [0.605, [0.996, 0.871, 0.539]], [0.607, [0.996, 0.863, 0.532]], [0.609, [0.996, 0.863, 0.532]], + [0.611, [0.996, 0.855, 0.526]], [0.613, [0.996, 0.855, 0.526]], [0.614, [0.995, 0.848, 0.519]], + [0.616, [0.995, 0.848, 0.519]], [0.618, [0.995, 0.84, 0.513]], [0.62, [0.995, 0.84, 0.513]], + [0.622, [0.995, 0.832, 0.506]], [0.624, [0.995, 0.832, 0.506]], [0.626, [0.995, 0.825, 0.5]], + [0.628, [0.995, 0.825, 0.5]], [0.63, [0.995, 0.817, 0.493]], [0.632, [0.995, 0.817, 0.493]], + [0.634, [0.995, 0.809, 0.487]], [0.636, [0.995, 0.809, 0.487]], + [0.638, [0.995, 0.802, 0.481]], [0.64, [0.995, 0.802, 0.481]], [0.642, [0.994, 0.794, 0.474]], + [0.644, [0.994, 0.794, 0.474]], [0.646, [0.994, 0.786, 0.468]], [0.648, [0.994, 0.786, 0.468]], + [0.65, [0.994, 0.778, 0.461]], [0.652, [0.994, 0.778, 0.461]], [0.654, [0.994, 0.771, 0.455]], + [0.656, [0.994, 0.771, 0.455]], [0.658, [0.994, 0.763, 0.448]], [0.659, [0.994, 0.763, 0.448]], + [0.661, [0.994, 0.755, 0.442]], [0.663, [0.994, 0.755, 0.442]], [0.665, [0.993, 0.748, 0.435]], + [0.667, [0.993, 0.748, 0.435]], [0.669, [0.993, 0.74, 0.429]], [0.671, [0.993, 0.74, 0.429]], + [0.673, [0.993, 0.732, 0.422]], [0.675, [0.993, 0.732, 0.422]], [0.677, [0.993, 0.725, 0.416]], + [0.679, [0.993, 0.725, 0.416]], [0.681, [0.993, 0.717, 0.409]], [0.683, [0.993, 0.717, 0.409]], + [0.685, [0.993, 0.709, 0.403]], [0.687, [0.993, 0.709, 0.403]], [0.689, [0.993, 0.702, 0.397]], + [0.691, [0.993, 0.702, 0.397]], [0.693, [0.992, 0.694, 0.39]], [0.695, [0.992, 0.694, 0.39]], + [0.697, [0.992, 0.686, 0.384]], [0.699, [0.992, 0.686, 0.384]], [0.701, [0.991, 0.677, 0.378]], + [0.703, [0.991, 0.677, 0.378]], [0.705, [0.99, 0.667, 0.373]], [0.706, [0.99, 0.667, 0.373]], + [0.708, [0.989, 0.657, 0.369]], [0.71, [0.989, 0.657, 0.369]], [0.712, [0.987, 0.647, 0.364]], + [0.714, [0.987, 0.647, 0.364]], [0.716, [0.986, 0.637, 0.36]], [0.718, [0.986, 0.637, 0.36]], + [0.72, [0.985, 0.627, 0.355]], [0.722, [0.985, 0.627, 0.355]], [0.724, [0.983, 0.617, 0.35]], + [0.726, [0.983, 0.617, 0.35]], [0.728, [0.982, 0.607, 0.346]], [0.73, [0.982, 0.607, 0.346]], + [0.732, [0.98, 0.597, 0.341]], [0.734, [0.98, 0.597, 0.341]], [0.736, [0.979, 0.587, 0.337]], + [0.738, [0.979, 0.587, 0.337]], [0.74, [0.978, 0.577, 0.332]], [0.742, [0.978, 0.577, 0.332]], + [0.744, [0.976, 0.567, 0.327]], [0.746, [0.976, 0.567, 0.327]], [0.748, [0.975, 0.557, 0.323]], + [0.75, [0.975, 0.557, 0.323]], [0.751, [0.973, 0.547, 0.318]], [0.753, [0.973, 0.547, 0.318]], + [0.755, [0.972, 0.537, 0.313]], [0.757, [0.972, 0.537, 0.313]], [0.759, [0.971, 0.527, 0.309]], + [0.761, [0.971, 0.527, 0.309]], [0.763, [0.969, 0.517, 0.304]], [0.765, [0.969, 0.517, 0.304]], + [0.767, [0.968, 0.507, 0.3]], [0.769, [0.968, 0.507, 0.3]], [0.771, [0.967, 0.497, 0.295]], + [0.773, [0.967, 0.497, 0.295]], [0.775, [0.965, 0.487, 0.29]], [0.777, [0.965, 0.487, 0.29]], + [0.779, [0.964, 0.477, 0.286]], [0.781, [0.964, 0.477, 0.286]], [0.783, [0.962, 0.467, 0.281]], + [0.785, [0.962, 0.467, 0.281]], [0.787, [0.961, 0.457, 0.277]], [0.789, [0.961, 0.457, 0.277]], + [0.791, [0.96, 0.447, 0.272]], [0.793, [0.96, 0.447, 0.272]], [0.795, [0.958, 0.437, 0.267]], + [0.796, [0.958, 0.437, 0.267]], [0.798, [0.957, 0.427, 0.263]], [0.8, [0.957, 0.427, 0.263]], + [0.802, [0.952, 0.418, 0.258]], [0.804, [0.952, 0.418, 0.258]], [0.806, [0.948, 0.409, 0.254]], + [0.808, [0.948, 0.409, 0.254]], [0.81, [0.943, 0.399, 0.25]], [0.812, [0.943, 0.399, 0.25]], + [0.814, [0.939, 0.39, 0.246]], [0.816, [0.939, 0.39, 0.246]], [0.818, [0.935, 0.381, 0.241]], + [0.82, [0.935, 0.381, 0.241]], [0.822, [0.93, 0.371, 0.237]], [0.824, [0.93, 0.371, 0.237]], + [0.826, [0.926, 0.362, 0.233]], [0.828, [0.926, 0.362, 0.233]], [0.83, [0.921, 0.352, 0.228]], + [0.832, [0.921, 0.352, 0.228]], [0.834, [0.917, 0.343, 0.224]], [0.836, [0.917, 0.343, 0.224]], + [0.838, [0.912, 0.334, 0.22]], [0.84, [0.912, 0.334, 0.22]], [0.841, [0.908, 0.324, 0.215]], + [0.843, [0.908, 0.324, 0.215]], [0.845, [0.903, 0.315, 0.211]], [0.847, [0.903, 0.315, 0.211]], + [0.849, [0.899, 0.305, 0.207]], [0.851, [0.899, 0.305, 0.207]], [0.853, [0.894, 0.296, 0.202]], + [0.855, [0.894, 0.296, 0.202]], [0.857, [0.89, 0.287, 0.198]], [0.859, [0.89, 0.287, 0.198]], + [0.861, [0.886, 0.277, 0.194]], [0.863, [0.886, 0.277, 0.194]], [0.865, [0.881, 0.268, 0.19]], + [0.867, [0.881, 0.268, 0.19]], [0.869, [0.877, 0.259, 0.185]], [0.871, [0.877, 0.259, 0.185]], + [0.873, [0.872, 0.249, 0.181]], [0.875, [0.872, 0.249, 0.181]], [0.877, [0.868, 0.24, 0.177]], + [0.879, [0.868, 0.24, 0.177]], [0.881, [0.863, 0.23, 0.172]], [0.883, [0.863, 0.23, 0.172]], + [0.885, [0.859, 0.221, 0.168]], [0.886, [0.859, 0.221, 0.168]], [0.888, [0.854, 0.212, 0.164]], + [0.89, [0.854, 0.212, 0.164]], [0.892, [0.85, 0.202, 0.159]], [0.894, [0.85, 0.202, 0.159]], + [0.896, [0.845, 0.193, 0.155]], [0.898, [0.845, 0.193, 0.155]], [0.9, [0.839, 0.185, 0.153]], + [0.902, [0.839, 0.185, 0.153]], [0.904, [0.832, 0.177, 0.153]], [0.906, [0.832, 0.177, 0.153]], + [0.908, [0.824, 0.17, 0.153]], [0.91, [0.824, 0.17, 0.153]], [0.912, [0.816, 0.162, 0.152]], + [0.914, [0.816, 0.162, 0.152]], [0.916, [0.809, 0.155, 0.152]], [0.918, [0.809, 0.155, 0.152]], + [0.92, [0.801, 0.148, 0.152]], [0.922, [0.801, 0.148, 0.152]], [0.924, [0.793, 0.14, 0.152]], + [0.926, [0.793, 0.14, 0.152]], [0.928, [0.785, 0.133, 0.152]], [0.93, [0.785, 0.133, 0.152]], + [0.932, [0.778, 0.125, 0.152]], [0.933, [0.778, 0.125, 0.152]], [0.935, [0.77, 0.118, 0.151]], + [0.937, [0.77, 0.118, 0.151]], [0.939, [0.762, 0.111, 0.151]], [0.941, [0.762, 0.111, 0.151]], + [0.943, [0.755, 0.103, 0.151]], [0.945, [0.755, 0.103, 0.151]], [0.947, [0.747, 0.096, 0.151]], + [0.949, [0.747, 0.096, 0.151]], [0.951, [0.739, 0.089, 0.151]], [0.953, [0.739, 0.089, 0.151]], + [0.955, [0.732, 0.081, 0.151]], [0.957, [0.732, 0.081, 0.151]], [0.959, [0.724, 0.074, 0.151]], + [0.961, [0.724, 0.074, 0.151]], [0.963, [0.716, 0.066, 0.15]], [0.965, [0.716, 0.066, 0.15]], + [0.967, [0.709, 0.059, 0.15]], [0.969, [0.709, 0.059, 0.15]], [0.971, [0.701, 0.052, 0.15]], + [0.973, [0.701, 0.052, 0.15]], [0.975, [0.693, 0.044, 0.15]], [0.977, [0.693, 0.044, 0.15]], + [0.978, [0.686, 0.037, 0.15]], [0.98, [0.686, 0.037, 0.15]], [0.982, [0.678, 0.03, 0.15]], + [0.984, [0.678, 0.03, 0.15]], [0.986, [0.67, 0.022, 0.149]], [0.988, [0.67, 0.022, 0.149]], + [0.99, [0.662, 0.015, 0.149]], [0.992, [0.662, 0.015, 0.149]], [0.994, [0.655, 0.007, 0.149]], + [0.996, [0.655, 0.007, 0.149]], [0.998, [0.647, 0, 0.149]], [1, [0.647, 0, 0.149]]]; diff --git a/src/ui/public/vislib/lib/layout/layout_types.js b/src/ui/public/vislib/lib/layout/layout_types.js index cc80b3857fe42..c6158d9a846e9 100644 --- a/src/ui/public/vislib/lib/layout/layout_types.js +++ b/src/ui/public/vislib/lib/layout/layout_types.js @@ -1,5 +1,6 @@ import { VislibLibLayoutTypesColumnLayoutProvider } from './types/column_layout'; import { VislibLibLayoutTypesPieLayoutProvider } from './types/pie_layout'; +import { GaugeLayoutProvider } from './types/gauge_layout'; export function VislibLibLayoutLayoutTypesProvider(Private) { @@ -13,6 +14,7 @@ export function VislibLibLayoutLayoutTypesProvider(Private) { */ return { pie: Private(VislibLibLayoutTypesPieLayoutProvider), + gauge: Private(GaugeLayoutProvider), point_series: Private(VislibLibLayoutTypesColumnLayoutProvider) }; } diff --git a/src/ui/public/vislib/lib/layout/splits/gauge_chart/chart_split.js b/src/ui/public/vislib/lib/layout/splits/gauge_chart/chart_split.js new file mode 100644 index 0000000000000..533c2f0dcd118 --- /dev/null +++ b/src/ui/public/vislib/lib/layout/splits/gauge_chart/chart_split.js @@ -0,0 +1,51 @@ +import d3 from 'd3'; +define(function () { + return function ChartSplitFactory() { + + /* + * Adds div DOM elements to the `.chart-wrapper` element based on the data layout. + * For example, if the data has rows, it returns the same number of + * `.chart` elements as row objects. + */ + + return function split(selection) { + selection.each(function (data) { + const div = d3.select(this) + .attr('class', function () { + if (data.rows) { + return 'chart-wrapper-row'; + } else if (data.columns) { + return 'chart-wrapper-column'; + } else { + return 'chart-wrapper'; + } + }); + let divClass; + + const charts = div.selectAll('charts') + .append('div') + .data(function (d) { + if (d.rows) { + divClass = 'chart-row'; + return d.rows; + } else if (d.columns) { + divClass = 'chart-column'; + return d.columns; + } else { + divClass = 'chart'; + return [d]; + } + }) + .enter() + .append('div') + .attr('class', function () { + return divClass; + }); + + if (!data.series) { + charts.call(split); + } + }); + }; + }; +}); diff --git a/src/ui/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js b/src/ui/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js new file mode 100644 index 0000000000000..d9c70f5ddff44 --- /dev/null +++ b/src/ui/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js @@ -0,0 +1,40 @@ +import d3 from 'd3'; +define(function () { + return function ChartTitleSplitFactory() { + + /* + * Adds div DOM elements to either the `.y-axis-chart-title` element or the + * `.x-axis-chart-title` element based on the data layout. + * For example, if the data has rows, it returns the same number of + * `.chart-title` elements as row objects. + * if not data.rows or data.columns, return no chart titles + */ + + return function (selection, parent) { + selection.each(function (data) { + const div = d3.select(this); + + if (!data.slices) { + div.selectAll('.chart-title') + .append('div') + .data(function (d) { + return d.rows ? d.rows : d.columns; + }) + .enter() + .append('div') + .attr('class', 'chart-title'); + + if (data.rows) { + d3.select(parent).select('.x-axis-chart-title').remove(); + } else { + d3.select(parent).select('.y-axis-chart-title').remove(); + } + + return div; + } + + return d3.select(this).remove(); + }); + }; + }; +}); diff --git a/src/ui/public/vislib/lib/layout/types/gauge_layout.js b/src/ui/public/vislib/lib/layout/types/gauge_layout.js new file mode 100644 index 0000000000000..05e35a8065a5d --- /dev/null +++ b/src/ui/public/vislib/lib/layout/types/gauge_layout.js @@ -0,0 +1,65 @@ +import GaugeChartSplitProvider from '../splits/gauge_chart/chart_split'; +//import VislibLibLayoutSplitsPieChartChartTitleSplitProvider from '../splits/gauge_chart/chart_title_split'; +export function GaugeLayoutProvider(Private) { + const chartSplit = Private(GaugeChartSplitProvider); + //const chartTitleSplit = Private(VislibLibLayoutSplitsPieChartChartTitleSplitProvider); + + /** + * Specifies the visualization layout for column charts. + * + * This is done using an array of objects. The first object has + * a `parent` DOM element, a DOM `type` (e.g. div, svg, etc), + * and a `class` (required). Each child can omit the parent object, + * but must include a type and class. + * + * Optionally, you can specify `datum` to be bound to the DOM + * element, a `splits` function that divides the selected element + * into more DOM elements based on a callback function provided, or + * a children array which nests other layout objects. + * + * Objects in children arrays are children of the current object and return + * DOM elements which are children of their respective parent element. + */ + + return function (el, data) { + if (!el || !data) { + throw new Error('Both an el and data need to be specified'); + } + + return [ + { + parent: el, + type: 'div', + class: 'vis-wrapper', + datum: data, + children: [ + { + type: 'div', + class: 'y-axis-chart-title', + //splits: chartTitleSplit + }, + { + type: 'div', + class: 'vis-col-wrapper', + children: [ + { + type: 'div', + class: 'chart-wrapper', + splits: chartSplit + }, + { + type: 'div', + class: 'vis-alerts' + }, + { + type: 'div', + class: 'x-axis-chart-title', + //splits: chartTitleSplit + } + ] + } + ] + } + ]; + }; +} diff --git a/src/ui/public/vislib/lib/types/gauge.js b/src/ui/public/vislib/lib/types/gauge.js new file mode 100644 index 0000000000000..d53bf491779b5 --- /dev/null +++ b/src/ui/public/vislib/lib/types/gauge.js @@ -0,0 +1,14 @@ +import _ from 'lodash'; + +export function vislibGaugeProvider() { + + return function (config) { + if (!config.chart) { + config.chart = _.defaults({}, config, { + type: 'gauge' + }); + } + + return config; + }; +} diff --git a/src/ui/public/vislib/lib/types/index.js b/src/ui/public/vislib/lib/types/index.js index adcedc172453f..9721e89677d2d 100644 --- a/src/ui/public/vislib/lib/types/index.js +++ b/src/ui/public/vislib/lib/types/index.js @@ -1,5 +1,6 @@ import { VislibTypesPointSeries } from './point_series'; import { VislibPieConfigProvider } from './pie'; +import { vislibGaugeProvider } from './gauge'; export function VislibTypesProvider(Private) { const pointSeries = Private(VislibTypesPointSeries); @@ -17,5 +18,6 @@ export function VislibTypesProvider(Private) { area: pointSeries.area, point_series: pointSeries.line, heatmap: pointSeries.heatmap, + gauge: Private(vislibGaugeProvider) }; } diff --git a/src/ui/public/vislib/styles/_svg.less b/src/ui/public/vislib/styles/_svg.less index a1fd676b93f0f..be46f39d23dab 100644 --- a/src/ui/public/vislib/styles/_svg.less +++ b/src/ui/public/vislib/styles/_svg.less @@ -17,6 +17,10 @@ fill: @svg-tick-text-color; } +.chart-text { + fill: @svg-tick-text-color; +} + /* Brush Styling */ .brush .extent { stroke: @svg-brush-color; diff --git a/src/ui/public/vislib/visualizations/gauge_chart.js b/src/ui/public/vislib/visualizations/gauge_chart.js new file mode 100644 index 0000000000000..a7a5363c0af34 --- /dev/null +++ b/src/ui/public/vislib/visualizations/gauge_chart.js @@ -0,0 +1,71 @@ +import d3 from 'd3'; +import $ from 'jquery'; +import { VislibVisualizationsChartProvider } from './_chart'; +import { GaugeTypesProvider } from './gauges/gauge_types'; + +export function GaugeChartProvider(Private) { + + const Chart = Private(VislibVisualizationsChartProvider); + const gaugeTypes = Private(GaugeTypesProvider); + + class GaugeChart extends Chart { + constructor(handler, chartEl, chartData) { + super(handler, chartEl, chartData); + this.gaugeConfig = handler.visConfig.get('gauge', {}); + this.gauge = new gaugeTypes[this.gaugeConfig.type](this); + } + + addEvents(element) { + const events = this.events; + + return element + .call(events.addHoverEvent()) + .call(events.addMouseoutEvent()) + .call(events.addClickEvent()); + } + + draw() { + const self = this; + const verticalSplit = this.gaugeConfig.verticalSplit; + + return function (selection) { + selection.each(function (data) { + const div = d3.select(this); + const width = verticalSplit ? $(this).width() : $(this).width() / data.series.length; + const height = (verticalSplit ? $(this).height() / data.series.length : $(this).height()) - 25; + const transformX = width / 2; + const transformY = self.gaugeConfig.gaugeType === 'Meter' ? height / 1.5 : height / 2; + + + + data.series.forEach(series => { + const svg = div.append('svg') + .attr('width', width) + .attr('height', height) + .style('display', 'inline-block') + .style('overflow', 'hidden'); + + const g = svg.append('g') + .attr('transform', `translate(${transformX}, ${transformY})`); + + const gauges = self.gauge.drawGauge(g, series, width, height); + self.addEvents(gauges); + }); + + div.append('div') + .attr('class', 'chart-title') + .style('text-align', 'center') + .text(data.label || data.yAxisLabel); + + self.events.emit('rendered', { + chart: data + }); + + return div; + }); + }; + } + } + + return GaugeChart; +} diff --git a/src/ui/public/vislib/visualizations/gauges/gauge_types.js b/src/ui/public/vislib/visualizations/gauges/gauge_types.js new file mode 100644 index 0000000000000..7d4c6b7de122b --- /dev/null +++ b/src/ui/public/vislib/visualizations/gauges/gauge_types.js @@ -0,0 +1,10 @@ +import { MeterGaugeProvider } from './meter'; +import { SimpleGaugeProvider } from './simple'; + +export function GaugeTypesProvider(Private) { + + return { + meter: Private(MeterGaugeProvider), + simple: Private(SimpleGaugeProvider), + }; +} diff --git a/src/ui/public/vislib/visualizations/gauges/meter.js b/src/ui/public/vislib/visualizations/gauges/meter.js new file mode 100644 index 0000000000000..01c02b6df5fbb --- /dev/null +++ b/src/ui/public/vislib/visualizations/gauges/meter.js @@ -0,0 +1,300 @@ +import d3 from 'd3'; +import _ from 'lodash'; +import { getHeatmapColors } from 'ui/vislib/components/color/heatmap_color'; + +export function MeterGaugeProvider() { + + + const defaultConfig = { + showTooltip: true, + percentageMode: true, + maxAngle: 2 * Math.PI * 1.3, + minAngle: 2 * Math.PI * 0.7, + innerSpace: 5, + extents: [0, 10000], + scale: { + show: true, + color: '#666', + width: 2, + ticks: 10, + tickLength: 8, + }, + labels: { + show: true, + color: '#666' + }, + style: { + bgWidth: 0.5, + width: 0.9 + } + }; + + class MeterGauge { + constructor(gaugeChart) { + this.gaugeChart = gaugeChart; + this.gaugeConfig = gaugeChart.gaugeConfig; + this.gaugeConfig = _.defaultsDeep(this.gaugeConfig, defaultConfig); + + this.gaugeChart.handler.visConfig.set('legend', { + labels: this.getLabels(), + colors: this.getColors() + }); + + const colors = this.gaugeChart.handler.visConfig.get('legend.colors', null); + if (colors) { + this.gaugeChart.handler.vis.uiState.setSilent('vis.defaultColors', null); + this.gaugeChart.handler.vis.uiState.setSilent('vis.defaultColors', colors); + } + + this.colorFunc = this.gaugeChart.handler.data.getColorFunc(); + } + + getLabels() { + const isPercentageMode = this.gaugeConfig.percentageMode; + const colorsRange = this.gaugeConfig.colorsRange; + const max = _.last(colorsRange).to; + const labels = []; + colorsRange.forEach(range => { + const from = isPercentageMode ? Math.round(100 * range.from / max) : range.from; + const to = isPercentageMode ? Math.round(100 * range.to / max) : range.to; + labels.push(`${from} - ${to}`); + }); + + return labels; + } + + getColors() { + const invertColors = this.gaugeConfig.invertColors; + const colorSchema = this.gaugeConfig.colorSchema; + const colorsRange = this.gaugeConfig.colorsRange; + const labels = this.getLabels(); + const colors = {}; + for (let i = 0; i < labels.length; i += 1) { + const divider = Math.max(colorsRange.length - 1, 1); + const val = invertColors ? 1 - i / divider : i / divider; + colors[labels[i]] = getHeatmapColors(val, colorSchema); + } + return colors; + } + + getBucket(val) { + let bucket = _.findIndex(this.gaugeConfig.colorsRange, range => { + return range.from <= val && range.to > val; + }); + + if (bucket === -1) { + if (val < this.gaugeConfig.colorsRange[0].from) bucket = 0; + else bucket = this.gaugeConfig.colorsRange.length - 1; + } + + return bucket; + } + + getLabel(val) { + const bucket = this.getBucket(val); + const labels = this.gaugeChart.handler.visConfig.get('legend.labels'); + return labels[bucket]; + } + + getColorBucket(val) { + const bucket = this.getBucket(val); + const labels = this.gaugeChart.handler.visConfig.get('legend.labels'); + return this.colorFunc(labels[bucket]); + } + + drawScale(svg, radius, angle) { + const scaleWidth = this.gaugeConfig.scale.width; + const tickLength = this.gaugeConfig.scale.tickLength; + const scaleTicks = this.gaugeConfig.scale.ticks; + + const scale = svg.append('g'); + + this.gaugeConfig.colorsRange.forEach(range => { + const color = this.getColorBucket(range.from); + + const scaleArc = d3.svg.arc() + .startAngle(angle(range.from)) + .endAngle(angle(range.to)) + .innerRadius(radius) + .outerRadius(radius + scaleWidth); + + scale + .append('path') + .attr('d', scaleArc) + .style('stroke', color) + .style('fill', color); + }); + + + const extents = angle.domain(); + for (let i = 0; i <= scaleTicks; i++) { + const val = i * (extents[1] - extents[0]) / scaleTicks; + const tickAngle = angle(val) - Math.PI / 2; + const x0 = Math.cos(tickAngle) * radius; + const x1 = Math.cos(tickAngle) * (radius - tickLength); + const y0 = Math.sin(tickAngle) * radius; + const y1 = Math.sin(tickAngle) * (radius - tickLength); + const color = this.getColorBucket(val); + scale.append('line') + .attr('x1', x0).attr('x2', x1) + .attr('y1', y0).attr('y2', y1) + .style('stroke-width', scaleWidth) + .style('stroke', color); + } + + return scale; + } + + drawGauge(svg, data, width, height) { + const marginFactor = 0.95; + const tooltip = this.gaugeChart.tooltip; + const isTooltip = this.gaugeChart.handler.visConfig.get('addTooltip'); + const maxAngle = this.gaugeConfig.maxAngle; + const minAngle = this.gaugeConfig.minAngle; + const angleFactor = this.gaugeConfig.gaugeType === 'Meter' ? 0.75 : 1; + const maxRadius = (Math.min(width, height / angleFactor) / 2) * marginFactor; + const yFieldFormatter = this.gaugeChart.handler.data.get('yAxisFormatter'); + + const extendRange = this.gaugeConfig.extendRange; + const maxY = _.max(data.values, 'y').y; + const min = this.gaugeConfig.colorsRange[0].from; + const max = _.last(this.gaugeConfig.colorsRange).to; + const angle = d3.scale.linear() + .range([minAngle, maxAngle]) + .domain([min, extendRange && max < maxY ? maxY : max]); + const radius = d3.scale.linear() + .range([0, maxRadius]) + .domain([this.gaugeConfig.innerSpace + 1, 0]); + + const totalWidth = Math.abs(radius(0) - radius(1)); + const bgPadding = totalWidth * (1 - this.gaugeConfig.style.bgWidth) / 2; + const gaugePadding = totalWidth * (1 - this.gaugeConfig.style.width) / 2; + const arc = d3.svg.arc() + .startAngle(minAngle) + .endAngle(function (d) { + return Math.max(0, Math.min(maxAngle, angle(d.y))); + }) + .innerRadius(function (d, i, j) { + return Math.max(0, radius(j + 1) + gaugePadding); + }) + .outerRadius(function (d, i, j) { + return Math.max(0, radius(j) - gaugePadding); + }); + + + const bgArc = d3.svg.arc() + .startAngle(minAngle) + .endAngle(maxAngle) + .innerRadius(function (d, i, j) { + return Math.max(0, radius(j + 1) + bgPadding); + }) + .outerRadius(function (d, i, j) { + return Math.max(0, radius(j) - bgPadding); + }); + + const gaugeHolders = svg + .selectAll('path') + .data([data]) + .enter() + .append('g') + .attr('data-label', (d) => this.getLabel(d.values[0].y)); + + + const gauges = gaugeHolders + .selectAll('g') + .data(d => d.values) + .enter(); + + + gauges + .append('path') + .attr('d', bgArc) + .style('fill', this.gaugeConfig.style.bgFill); + + const self = this; + const series = gauges + .append('path') + .attr('d', arc) + .style('fill', function (d) { + return self.getColorBucket(d.y); + }); + + const smallContainer = svg.node().getBBox().height < 70; + let hiddenLabels = smallContainer; + + if (this.gaugeConfig.labels.show) { + svg + .append('text') + .attr('class', 'chart-label') + .text(data.label) + .attr('y', -30) + .attr('style', 'dominant-baseline: central; text-anchor: middle;') + .style('display', function () { + const textLength = this.getBBox().width; + const textTooLong = textLength > maxRadius; + if (textTooLong) { + hiddenLabels = true; + } + return smallContainer || textTooLong ? 'none' : 'initial'; + }); + + svg + .append('text') + .attr('class', 'chart-label') + .text(this.gaugeConfig.style.subText) + .attr('y', 20) + .attr('style', 'dominant-baseline: central; text-anchor: middle;') + .style('display', function () { + const textLength = this.getBBox().width; + const textTooLong = textLength > maxRadius; + if (textTooLong) { + hiddenLabels = true; + } + return smallContainer || textTooLong ? 'none' : 'initial'; + }); + + gauges + .append('text') + .attr('class', 'chart-label') + .attr('y', -5) + .text(d => { + if (this.gaugeConfig.percentageMode) { + const percentage = Math.round(100 * (d.y - min) / (max - min)); + return `${percentage}%`; + } + return yFieldFormatter(d.y); + }) + .attr('style', 'dominant-baseline: central;') + .style('text-anchor', 'middle') + .style('font-size', '2em') + .style('display', function () { + const textLength = this.getBBox().width; + const textTooLong = textLength > maxRadius; + if (textTooLong) { + hiddenLabels = true; + } + return textTooLong ? 'none' : 'initial'; + }); + } + + if (this.gaugeConfig.scale.show) { + this.drawScale(svg, radius(1), angle); + } + + if (isTooltip) { + series.each(function () { + const gauge = d3.select(this); + gauge.call(tooltip.render()); + }); + } + + if (hiddenLabels) { + this.gaugeChart.handler.alerts.show('Some labels were hidden due to size constrains'); + } + + return series; + } + } + + return MeterGauge; +} diff --git a/src/ui/public/vislib/visualizations/gauges/simple.js b/src/ui/public/vislib/visualizations/gauges/simple.js new file mode 100644 index 0000000000000..ab8c98ab99949 --- /dev/null +++ b/src/ui/public/vislib/visualizations/gauges/simple.js @@ -0,0 +1,207 @@ +import d3 from 'd3'; +import _ from 'lodash'; +import { getHeatmapColors } from 'ui/vislib/components/color/heatmap_color'; + +export function SimpleGaugeProvider() { + + + const defaultConfig = { + showTooltip: true, + percentageMode: true, + extents: [0, 10000], + scale: { + show: true, + color: '#666', + width: 2, + ticks: 10, + tickLength: 5, + }, + labels: { + show: true, + color: '#666' + }, + style: { + bgColor: true, + bgFill: '#666' + } + }; + + class SimpleGauge { + constructor(gaugeChart) { + this.gaugeChart = gaugeChart; + this.gaugeConfig = gaugeChart.gaugeConfig; + this.gaugeConfig = _.defaultsDeep(this.gaugeConfig, defaultConfig); + this.randomNumber = Math.round(Math.random() * 100000); + + this.gaugeChart.handler.visConfig.set('legend', { + labels: this.getLabels(), + colors: this.getColors() + }); + + const colors = this.gaugeChart.handler.visConfig.get('legend.colors', null); + if (colors) { + this.gaugeChart.handler.vis.uiState.setSilent('vis.defaultColors', null); + this.gaugeChart.handler.vis.uiState.setSilent('vis.defaultColors', colors); + } + + this.colorFunc = this.gaugeChart.handler.data.getColorFunc(); + } + + getLabels() { + const isPercentageMode = this.gaugeConfig.percentageMode; + const colorsRange = this.gaugeConfig.colorsRange; + const max = _.last(colorsRange).to; + const labels = []; + colorsRange.forEach(range => { + const from = isPercentageMode ? Math.round(100 * range.from / max) : range.from; + const to = isPercentageMode ? Math.round(100 * range.to / max) : range.to; + labels.push(`${from} - ${to}`); + }); + + return labels; + } + + getColors() { + const invertColors = this.gaugeConfig.invertColors; + const colorSchema = this.gaugeConfig.colorSchema; + const colorsRange = this.gaugeConfig.colorsRange; + const labels = this.getLabels(); + const colors = {}; + for (let i = 0; i < labels.length; i += 1) { + const divider = Math.max(colorsRange.length - 1, 1); + const val = invertColors ? 1 - i / divider : i / divider; + colors[labels[i]] = getHeatmapColors(val, colorSchema); + } + return colors; + } + + getBucket(val) { + let bucket = _.findIndex(this.gaugeConfig.colorsRange, range => { + return range.from <= val && range.to > val; + }); + + if (bucket === -1) { + if (val < this.gaugeConfig.colorsRange[0].from) bucket = 0; + else bucket = this.gaugeConfig.colorsRange.length - 1; + } + + return bucket; + } + + getLabel(val) { + const bucket = this.getBucket(val); + const labels = this.gaugeChart.handler.visConfig.get('legend.labels'); + return labels[bucket]; + } + + getColorBucket(val) { + const bucket = this.getBucket(val); + const labels = this.gaugeChart.handler.visConfig.get('legend.labels'); + return this.colorFunc(labels[bucket]); + } + + drawGauge(svg, data, width) { + const tooltip = this.gaugeChart.tooltip; + const isTooltip = this.gaugeChart.handler.visConfig.get('addTooltip'); + const yFieldFormatter = this.gaugeChart.handler.data.get('yAxisFormatter'); + const fontSize = this.gaugeChart.handler.visConfig.get('gauge.style.fontSize'); + + const labelColor = this.gaugeConfig.style.labelColor; + const bgColor = this.gaugeConfig.style.bgColor; + const bgFill = this.gaugeConfig.style.bgFill; + const min = this.gaugeConfig.colorsRange[0].from; + const max = _.last(this.gaugeConfig.colorsRange).to; + + const gaugeHolders = svg + .selectAll('path') + .data([data]) + .enter() + .append('g') + .attr('data-label', (d) => this.getLabel(d.values[0].y)); + + + const gauges = gaugeHolders + .selectAll('g') + .data(d => d.values) + .enter(); + + + const self = this; + const series = gauges + .append('rect') + .attr('x', '-50%') + .attr('y', '-50%') + .attr('width', '99%') + .attr('height', '99%') + .style('fill', function (d) { + return bgColor ? self.getColorBucket(d.y) : 'transparent'; + }); + + const smallContainer = svg.node().getBBox().height < 70; + let hiddenLabels = smallContainer; + + const isTextTooLong = function () { + const textLength = this.getBBox().width; + const textTooLong = textLength > width; + if (textTooLong) { + hiddenLabels = true; + } + return smallContainer || textTooLong ? 'none' : 'initial'; + }; + + + if (this.gaugeConfig.labels.show) { + svg + .append('text') + .attr('class', 'chart-label') + .text(data.label) + .attr('y', Math.min(-25, -fontSize)) + .attr('style', 'dominant-baseline: central; text-anchor: middle;') + .style('display', isTextTooLong) + .style('fill', bgFill); + + svg + .append('text') + .attr('class', 'chart-label') + .text(this.gaugeConfig.style.subText) + .attr('y', Math.max(15, fontSize)) + .attr('style', 'dominant-baseline: central; text-anchor: middle;') + .style('display', isTextTooLong) + .style('fill', bgFill); + } + + gauges + .append('text') + .attr('class', 'chart-label') + .attr('y', -5) + .text(d => { + if (this.gaugeConfig.percentageMode) { + const percentage = Math.round(100 * (d.y - min) / (max - min)); + return `${percentage}%`; + } + return yFieldFormatter(d.y); + }) + .attr('style', 'dominant-baseline: central; font-weight: bold; white-space: nowrap;text-overflow: ellipsis;overflow: hidden;') + .style('text-anchor', 'middle') + .style('font-size', fontSize + 'pt') + .style('fill', function () { + return !bgColor && labelColor ? self.getColorBucket(data.values[0].y) : bgFill; + }); + + if (isTooltip) { + series.each(function () { + const gauge = d3.select(this); + gauge.call(tooltip.render()); + }); + } + + if (hiddenLabels) { + this.gaugeChart.handler.alerts.show('Some labels were hidden due to size constrains'); + } + + return series; + } + } + + return SimpleGauge; +} diff --git a/src/ui/public/vislib/visualizations/vis_types.js b/src/ui/public/vislib/visualizations/vis_types.js index fa7a99113b819..a1219f7ea997b 100644 --- a/src/ui/public/vislib/visualizations/vis_types.js +++ b/src/ui/public/vislib/visualizations/vis_types.js @@ -1,5 +1,6 @@ import { VislibVisualizationsPointSeriesProvider } from './point_series'; import { VislibVisualizationsPieChartProvider } from './pie_chart'; +import { GaugeChartProvider } from './gauge_chart'; export function VislibVisualizationsVisTypesProvider(Private) { @@ -13,6 +14,7 @@ export function VislibVisualizationsVisTypesProvider(Private) { */ return { pie: Private(VislibVisualizationsPieChartProvider), - point_series: Private(VislibVisualizationsPointSeriesProvider) + point_series: Private(VislibVisualizationsPointSeriesProvider), + gauge: Private(GaugeChartProvider) }; } diff --git a/src/ui/public/vislib_vis_type/vislib_vis_type.js b/src/ui/public/vislib_vis_type/vislib_vis_type.js index daa2bcc40a39a..e6100de6a1357 100644 --- a/src/ui/public/vislib_vis_type/vislib_vis_type.js +++ b/src/ui/public/vislib_vis_type/vislib_vis_type.js @@ -5,6 +5,7 @@ import 'plugins/kbn_vislib_vis_types/controls/point_series_options'; import 'plugins/kbn_vislib_vis_types/controls/line_interpolation_option'; import 'plugins/kbn_vislib_vis_types/controls/heatmap_options'; import 'plugins/kbn_vislib_vis_types/controls/point_series'; +import 'plugins/kbn_vislib_vis_types/controls/gauge_options'; import { VisVisTypeProvider } from 'ui/vis/vis_type'; import { AggResponsePointSeriesProvider } from 'ui/agg_response/point_series/point_series'; import VislibVisTypeVislibRenderbotProvider from 'ui/vislib_vis_type/vislib_renderbot'; diff --git a/src/ui/public/visualize/visualize_legend.js b/src/ui/public/visualize/visualize_legend.js index 080fac6e3856c..428fe835114fc 100644 --- a/src/ui/public/visualize/visualize_legend.js +++ b/src/ui/public/visualize/visualize_legend.js @@ -113,7 +113,7 @@ uiModules.get('kibana') $scope.open = $scope.vis.params.addLegend; } - if (vislibVis.visConfigArgs.type === 'heatmap') { + if (['heatmap', 'gauge'].includes(vislibVis.visConfigArgs.type)) { const labels = vislibVis.getLegendLabels(); if (labels) { $scope.labels = _.map(labels, label => { diff --git a/test/functional/apps/visualize/_chart_types.js b/test/functional/apps/visualize/_chart_types.js index 30644869b70b5..c5c0f591c89be 100644 --- a/test/functional/apps/visualize/_chart_types.js +++ b/test/functional/apps/visualize/_chart_types.js @@ -22,6 +22,8 @@ export default function ({ getService, getPageObjects }) { 'Pie', 'Vertical Bar', 'Data Table', + 'Gauge', + 'Goal', 'Metric', 'Tile Map', 'Timelion', diff --git a/test/functional/apps/visualize/_gauge_chart.js b/test/functional/apps/visualize/_gauge_chart.js new file mode 100644 index 0000000000000..364edaa552db0 --- /dev/null +++ b/test/functional/apps/visualize/_gauge_chart.js @@ -0,0 +1,74 @@ +import expect from 'expect.js'; + +export default function ({ getService, getPageObjects }) { + const log = getService('log'); + const retry = getService('retry'); + const screenshots = getService('screenshots'); + const PageObjects = getPageObjects(['common', 'visualize', 'header']); + + describe('visualize app', function describeIndexTests() { + const fromTime = '2015-09-19 06:31:44.000'; + const toTime = '2015-09-23 18:31:44.000'; + + before(function () { + log.debug('navigateToApp visualize'); + return PageObjects.common.navigateToUrl('visualize', 'new') + .then(function () { + log.debug('clickGauge'); + return PageObjects.visualize.clickGauge(); + }) + .then(function clickNewSearch() { + return PageObjects.visualize.clickNewSearch(); + }) + .then(function setAbsoluteRange() { + log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"'); + return PageObjects.header.setAbsoluteRange(fromTime, toTime); + }); + }); + + describe('gauge chart', function indexPatternCreation() { + + it('should show Count', function () { + const expectedCount = ['14,004', 'Count']; + + // initial metric of "Count" is selected by default + return retry.try(function tryingForTime() { + return PageObjects.visualize.getGaugeValue() + .then(function (metricValue) { + screenshots.take('Visualize-gauge-chart'); + expect(expectedCount).to.eql(metricValue[0].split('\n')); + }); + }); + }); + + it('should show Split Gauges', function () { + const expectedTexts = [ 'win 8', 'win xp', 'win 7', 'ios', 'osx' ]; + return PageObjects.visualize.clickMetricEditor() + .then(function clickBucket() { + log.debug('Bucket = Split Group'); + return PageObjects.visualize.clickBucket('Split Group'); + }) + .then(function selectAggregation() { + log.debug('Aggregation = Terms'); + return PageObjects.visualize.selectAggregation('Terms'); + }) + .then(function selectField() { + log.debug('Field = machine.os.raw'); + return PageObjects.visualize.selectField('machine.os.raw'); + }) + .then(function clickGo() { + return PageObjects.visualize.clickGo(); + }) + .then(function () { + return retry.try(function tryingForTime() { + return PageObjects.visualize.getGaugeValue() + .then(function (metricValue) { + expect(expectedTexts).to.eql(metricValue); + }); + }); + }); + }); + + }); + }); +} diff --git a/test/functional/apps/visualize/_metric_chart.js b/test/functional/apps/visualize/_metric_chart.js deleted file mode 100644 index 401d69eb51932..0000000000000 --- a/test/functional/apps/visualize/_metric_chart.js +++ /dev/null @@ -1,263 +0,0 @@ -import expect from 'expect.js'; - -export default function ({ getService, getPageObjects }) { - const log = getService('log'); - const retry = getService('retry'); - const screenshots = getService('screenshots'); - const PageObjects = getPageObjects(['common', 'visualize', 'header']); - - describe('visualize app', function describeIndexTests() { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; - - before(function () { - log.debug('navigateToApp visualize'); - return PageObjects.common.navigateToUrl('visualize', 'new') - .then(function () { - log.debug('clickMetric'); - return PageObjects.visualize.clickMetric(); - }) - .then(function clickNewSearch() { - return PageObjects.visualize.clickNewSearch(); - }) - .then(function setAbsoluteRange() { - log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"'); - return PageObjects.header.setAbsoluteRange(fromTime, toTime); - }); - }); - - describe('metric chart', function indexPatternCreation() { - - it('should show Count', function () { - const expectedCount = ['14,004', 'Count']; - - // initial metric of "Count" is selected by default - return retry.try(function tryingForTime() { - return PageObjects.visualize.getMetric() - .then(function (metricValue) { - screenshots.take('Visualize-metric-chart'); - expect(expectedCount).to.eql(metricValue.split('\n')); - }); - }); - }); - - it('should show Average', function () { - const avgMachineRam = ['13,104,036,080.615', 'Average machine.ram']; - return PageObjects.visualize.clickMetricEditor() - .then(function () { - log.debug('Aggregation = Average'); - return PageObjects.visualize.selectAggregation('Average'); - }) - .then(function selectField() { - log.debug('Field = machine.ram'); - return PageObjects.visualize.selectField('machine.ram', 'metrics'); - }) - .then(function clickGo() { - return PageObjects.visualize.clickGo(); - }) - .then(function () { - return retry.try(function tryingForTime() { - return PageObjects.visualize.getMetric() - .then(function (metricValue) { - expect(avgMachineRam).to.eql(metricValue.split('\n')); - }); - }); - }); - }); - - it('should show Sum', function () { - const sumPhpMemory = ['85,865,880', 'Sum of phpmemory']; - log.debug('Aggregation = Sum'); - return PageObjects.visualize.selectAggregation('Sum') - .then(function selectField() { - log.debug('Field = phpmemory'); - return PageObjects.visualize.selectField('phpmemory', 'metrics'); - }) - .then(function clickGo() { - return PageObjects.visualize.clickGo(); - }) - .then(function () { - return retry.try(function tryingForTime() { - return PageObjects.visualize.getMetric() - .then(function (metricValue) { - expect(sumPhpMemory).to.eql(metricValue.split('\n')); - }); - }); - }); - }); - - it('should show Median', function () { - const medianBytes = ['5,565.263', '50th percentile of bytes']; - // For now, only comparing the text label part of the metric - log.debug('Aggregation = Median'); - return PageObjects.visualize.selectAggregation('Median') - .then(function selectField() { - log.debug('Field = bytes'); - return PageObjects.visualize.selectField('bytes', 'metrics'); - }) - .then(function clickGo() { - return PageObjects.visualize.clickGo(); - }) - .then(function () { - return retry.try(function tryingForTime() { - return PageObjects.visualize.getMetric() - .then(function (metricValue) { - // only comparing the text label! - expect(medianBytes[1]).to.eql(metricValue.split('\n')[1]); - }); - }); - }); - }); - - it('should show Min', function () { - const minTimestamp = ['September 20th 2015, 00:00:00.000', 'Min @timestamp']; - log.debug('Aggregation = Min'); - return PageObjects.visualize.selectAggregation('Min') - .then(function selectField() { - log.debug('Field = @timestamp'); - return PageObjects.visualize.selectField('@timestamp', 'metrics'); - }) - .then(function clickGo() { - return PageObjects.visualize.clickGo(); - }) - .then(function () { - return retry.try(function tryingForTime() { - return PageObjects.visualize.getMetric() - .then(function (metricValue) { - expect(minTimestamp).to.eql(metricValue.split('\n')); - }); - }); - }); - }); - - it('should show Max', function () { - const maxRelatedContentArticleModifiedTime = ['April 4th 2015, 00:54:41.000', 'Max relatedContent.article:modified_time']; - log.debug('Aggregation = Max'); - return PageObjects.visualize.selectAggregation('Max') - .then(function selectField() { - log.debug('Field = relatedContent.article:modified_time'); - return PageObjects.visualize.selectField('relatedContent.article:modified_time', 'metrics'); - }) - .then(function clickGo() { - return PageObjects.visualize.clickGo(); - }) - .then(function () { - return retry.try(function tryingForTime() { - return PageObjects.visualize.getMetric() - .then(function (metricValue) { - expect(maxRelatedContentArticleModifiedTime).to.eql(metricValue.split('\n')); - }); - }); - }); - }); - - it('should show Standard Deviation', function () { - const standardDeviationBytes = [ - '-1,435.138', 'Lower Standard Deviation of bytes', - '12,889.766', 'Upper Standard Deviation of bytes' - ]; - log.debug('Aggregation = Standard Deviation'); - return PageObjects.visualize.selectAggregation('Standard Deviation') - .then(function selectField() { - log.debug('Field = bytes'); - return PageObjects.visualize.selectField('bytes', 'metrics'); - }) - .then(function clickGo() { - return PageObjects.visualize.clickGo(); - }) - .then(function () { - return retry.try(function tryingForTime() { - return PageObjects.visualize.getMetric() - .then(function (metricValue) { - expect(standardDeviationBytes).to.eql(metricValue.split('\n')); - }); - }); - }); - }); - - it('should show Unique Count', function () { - const uniqueCountClientip = ['1,000', 'Unique count of clientip']; - log.debug('Aggregation = Unique Count'); - return PageObjects.visualize.selectAggregation('Unique Count') - .then(function selectField() { - log.debug('Field = clientip'); - return PageObjects.visualize.selectField('clientip', 'metrics'); - }) - .then(function clickGo() { - return PageObjects.visualize.clickGo(); - }) - .then(function () { - return retry.try(function tryingForTime() { - return PageObjects.visualize.getMetric() - .then(function (metricValue) { - expect(uniqueCountClientip).to.eql(metricValue.split('\n')); - }); - }); - }) - .then(function () { - return PageObjects.visualize.getMetric() - .then(function (metricValue) { - log.debug('metricValue=' + metricValue.split('\n')); - expect(uniqueCountClientip).to.eql(metricValue.split('\n')); - }); - }); - }); - - it('should show Percentiles', function () { - const percentileMachineRam = [ - '2,147,483,648', '1st percentile of machine.ram', - '3,221,225,472', '5th percentile of machine.ram', - '7,516,192,768', '25th percentile of machine.ram', - '12,884,901,888', '50th percentile of machine.ram', - '18,253,611,008', '75th percentile of machine.ram', - '32,212,254,720', '95th percentile of machine.ram', - '32,212,254,720', '99th percentile of machine.ram' - ]; - - log.debug('Aggregation = Percentiles'); - return PageObjects.visualize.selectAggregation('Percentiles') - .then(function selectField() { - log.debug('Field = machine.ram'); - return PageObjects.visualize.selectField('machine.ram', 'metrics'); - }) - .then(function clickGo() { - return PageObjects.visualize.clickGo(); - }) - .then(function () { - return retry.try(function tryingForTime() { - return PageObjects.visualize.getMetric() - .then(function (metricValue) { - expect(percentileMachineRam).to.eql(metricValue.split('\n')); - }); - }); - }); - }); - - it('should show Percentile Ranks', function () { - const percentileRankBytes = [ '2.036%', 'Percentile rank 99 of "memory"']; - log.debug('Aggregation = Percentile Ranks'); - return PageObjects.visualize.selectAggregation('Percentile Ranks') - .then(function selectField() { - log.debug('Field = bytes'); - return PageObjects.visualize.selectField('memory', 'metrics'); - }) - .then(function selectField() { - log.debug('Values = 99'); - return PageObjects.visualize.setValue('99'); - }) - .then(function clickGo() { - return PageObjects.visualize.clickGo(); - }) - .then(function () { - return retry.try(function tryingForTime() { - return PageObjects.visualize.getMetric() - .then(function (metricValue) { - expect(percentileRankBytes).to.eql(metricValue.split('\n')); - }); - }); - }); - }); - - }); - }); -} diff --git a/test/functional/apps/visualize/index.js b/test/functional/apps/visualize/index.js index 7f1235ee18d11..1c47c1717c076 100644 --- a/test/functional/apps/visualize/index.js +++ b/test/functional/apps/visualize/index.js @@ -24,10 +24,10 @@ export default function ({ getService, loadTestFile }) { loadTestFile(require.resolve('./_editor')); loadTestFile(require.resolve('./_chart_types')); + loadTestFile(require.resolve('./_gauge_chart')); loadTestFile(require.resolve('./_area_chart')); loadTestFile(require.resolve('./_line_chart')); loadTestFile(require.resolve('./_data_table')); - loadTestFile(require.resolve('./_metric_chart')); loadTestFile(require.resolve('./_pie_chart')); loadTestFile(require.resolve('./_tag_cloud')); loadTestFile(require.resolve('./_tile_map')); diff --git a/test/functional/page_objects/visualize_page.js b/test/functional/page_objects/visualize_page.js index 834fdf519c6ff..61465f1430a4e 100644 --- a/test/functional/page_objects/visualize_page.js +++ b/test/functional/page_objects/visualize_page.js @@ -50,6 +50,13 @@ export function VisualizePageProvider({ getService, getPageObjects }) { .click(); } + clickGauge() { + return remote + .setFindTimeout(defaultFindTimeout) + .findByPartialLinkText('Gauge') + .click(); + } + clickPieChart() { return remote .setFindTimeout(defaultFindTimeout) @@ -167,6 +174,13 @@ export function VisualizePageProvider({ getService, getPageObjects }) { .getVisibleText(); } + getGaugeValue() { + return remote + .setFindTimeout(2000) + .findAllByCssSelector('visualize .chart svg') + .getVisibleText(); + } + clickMetricEditor() { return remote .setFindTimeout(defaultFindTimeout)