Merge branch 'master' of github.com:elastic/kibana
BIN
docs/maps/images/extended_stats_config.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
docs/maps/images/gear_icon.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 665 B |
BIN
docs/maps/images/quantitative_data_driven_styling.png
Normal file
After Width: | Height: | Size: 548 KiB |
Before Width: | Height: | Size: 3.8 MiB After Width: | Height: | Size: 522 KiB |
Before Width: | Height: | Size: 3.5 MiB After Width: | Height: | Size: 990 KiB |
Before Width: | Height: | Size: 3.5 MiB After Width: | Height: | Size: 1,014 KiB |
|
@ -5,6 +5,7 @@
|
|||
When styling a vector layer, you can customize your data by property, such as size and color.
|
||||
For each property, you can specify whether to use a constant or data driven value for the style.
|
||||
|
||||
|
||||
[float]
|
||||
[[maps-vector-style-static]]
|
||||
==== Static styling
|
||||
|
@ -17,24 +18,13 @@ The *kibana_sample_data_logs* layer uses static styling for all properties.
|
|||
[role="screenshot"]
|
||||
image::maps/images/vector_style_static.png[]
|
||||
|
||||
|
||||
[float]
|
||||
[[maps-vector-style-data-driven]]
|
||||
==== Data driven styling
|
||||
|
||||
Use data driven styling to symbolize features from a range of numeric property values.
|
||||
To enable data driven styling, click image:maps/images/gs_link_icon.png[] next to the property.
|
||||
This button is only available when vector features contain numeric properties.
|
||||
|
||||
When the property value is undefined for a feature:
|
||||
|
||||
* *Fill color* and *Border color* are set to transparent and are not visible.
|
||||
* *Border width* and *Symbol size* are set to the minimum size.
|
||||
* *Symbol orientation* is set to 0.
|
||||
|
||||
When the minimum and maximum are the same and there is no range:
|
||||
|
||||
* *Fill color* and *Border color* are set to last color in the color ramp.
|
||||
* *Border width* and *Symbol size* are set to the maximum size.
|
||||
Use data driven styling to symbolize features by property values.
|
||||
To enable data driven styling for a style property, change the selected value from *Fixed* or *Solid* to *By value*.
|
||||
|
||||
The image below shows an example of data driven styling using the <<add-sample-data, Kibana sample web logs>> data set.
|
||||
The *kibana_sample_data_logs* layer uses data driven styling for fill color and symbol size style properties.
|
||||
|
@ -49,6 +39,55 @@ Smaller circles symbolize documents with smaller payloads, and larger circles sy
|
|||
image::maps/images/vector_style_dynamic.png[]
|
||||
|
||||
|
||||
[float]
|
||||
[[maps-vector-style-quantitative-data-driven]]
|
||||
==== Quantitative data driven styling
|
||||
|
||||
Quantitative data driven styling symbolizes features from a range of numeric property values.
|
||||
|
||||
To ensure symbols are consistent as you pan, zoom, and filter the map, quantitative data driven styling uses {ref}/search-aggregations-metrics-extendedstats-aggregation.html[extended_stats aggregation] to retrieve statistical metadata.
|
||||
|
||||
Click the gear icon image:maps/images/gear_icon.png[] to configure extended_stats. Set *Sigma* to a smaller value to minimize outliers by moving the range minimum and maximum closer to the average. Clear the *Calculate range from indices* checkbox to turn off the extended_stats aggregation request.
|
||||
|
||||
NOTE: When the *Calculate range from indices* checkbox is cleared, symbols might be inconsistent as users pan, zoom, and filter the map. Without extended_stats, the range is calulated with data from the local layer. The range is recalulcated when layer data changes.
|
||||
|
||||
[role="screenshot"]
|
||||
image::maps/images/extended_stats_config.png[]
|
||||
|
||||
When the property value is undefined for a feature:
|
||||
|
||||
* *Fill color* and *Border color* are set to transparent and are not visible.
|
||||
* *Border width* and *Symbol size* are set to the minimum size.
|
||||
* *Symbol orientation* is set to 0.
|
||||
|
||||
When the symbol range minimum and maximum are the same and there is no range:
|
||||
|
||||
* *Fill color* and *Border color* are set to last color in the color ramp.
|
||||
* *Border width* and *Symbol size* are set to the maximum size.
|
||||
|
||||
|
||||
[float]
|
||||
[[maps-vector-style-qualitative-data-driven]]
|
||||
==== Qualitative data driven styling
|
||||
|
||||
Qualitative data driven styling symbolizes non-numeric properties, such as strings and IP addresses, by category.
|
||||
|
||||
Qualitative data driven styling is available for the following styling properties:
|
||||
|
||||
* *Fill color*
|
||||
* *Border color*
|
||||
* *Label color*
|
||||
* *Label border color*
|
||||
|
||||
Qualitative data driven styling uses a {ref}/search-aggregations-bucket-terms-aggregation.html[terms aggregation] to retrieve the top nine categories for the property. Feature values within the top categories are assigned a unique color. Feature values outside of the top categories are grouped into the *Other* category. A feature is assigned the *Other* category when the property value is undefined.
|
||||
|
||||
The image below shows an example of quantitative data driven styling using the <<add-sample-data, Kibana sample web logs>> data set.
|
||||
The `machine.os.keyword` property determines the color of each symbol based on category.
|
||||
|
||||
[role="screenshot"]
|
||||
image::maps/images/quantitative_data_driven_styling.png[]
|
||||
|
||||
|
||||
[float]
|
||||
[[maps-vector-style-class]]
|
||||
==== Class styling
|
||||
|
|
|
@ -58,6 +58,7 @@ export default {
|
|||
'^ui/(.*)': '<rootDir>/src/legacy/ui/public/$1',
|
||||
'^uiExports/(.*)': '<rootDir>/src/dev/jest/mocks/file_mock.js',
|
||||
'^test_utils/(.*)': '<rootDir>/src/test_utils/public/$1',
|
||||
'^fixtures/(.*)': '<rootDir>/src/fixtures/$1',
|
||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
||||
'<rootDir>/src/dev/jest/mocks/file_mock.js',
|
||||
'\\.(css|less|scss)$': '<rootDir>/src/dev/jest/mocks/style_mock.js',
|
||||
|
|
|
@ -13,7 +13,7 @@ Object {
|
|||
"metrics": undefined,
|
||||
},
|
||||
"metric": Object {
|
||||
"colorSchema": "\\"Green to Red\\"",
|
||||
"colorSchema": "Green to Red",
|
||||
"colorsRange": "{range from=0 to=10000}",
|
||||
"invertColors": false,
|
||||
"labels": Object {
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import $ from 'jquery';
|
||||
import ngMock from 'ng_mock';
|
||||
import expect from '@kbn/expect';
|
||||
|
||||
import { Vis } from 'ui/vis';
|
||||
import LogstashIndexPatternStubProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
|
||||
import { start as visualizations } from '../../../visualizations/public/np_ready/public/legacy';
|
||||
|
||||
describe('metric_vis - createMetricVisTypeDefinition', () => {
|
||||
let setup = null;
|
||||
let vis;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(
|
||||
ngMock.inject(Private => {
|
||||
setup = () => {
|
||||
const metricVisType = visualizations.types.get('metric');
|
||||
const indexPattern = Private(LogstashIndexPatternStubProvider);
|
||||
|
||||
indexPattern.stubSetFieldFormat('ip', 'url', {
|
||||
urlTemplate: 'http://ip.info?address={{value}}',
|
||||
labelTemplate: 'ip[{{value}}]',
|
||||
});
|
||||
|
||||
vis = new Vis(indexPattern, {
|
||||
type: 'metric',
|
||||
aggs: [{ id: '1', type: 'top_hits', schema: 'metric', params: { field: 'ip' } }],
|
||||
});
|
||||
|
||||
vis.params.dimensions = {
|
||||
metrics: [
|
||||
{
|
||||
accessor: 0,
|
||||
format: {
|
||||
id: 'url',
|
||||
params: {
|
||||
urlTemplate: 'http://ip.info?address={{value}}',
|
||||
labelTemplate: 'ip[{{value}}]',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const el = document.createElement('div');
|
||||
const Controller = metricVisType.visualization;
|
||||
const controller = new Controller(el, vis);
|
||||
const render = esResponse => {
|
||||
controller.render(esResponse, vis.params);
|
||||
};
|
||||
|
||||
return { el, render };
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
it('renders html value from field formatter', () => {
|
||||
const { el, render } = setup();
|
||||
|
||||
const ip = '235.195.237.208';
|
||||
render({
|
||||
columns: [{ id: 'col-0', name: 'ip' }],
|
||||
rows: [{ 'col-0': 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}]`);
|
||||
});
|
||||
});
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { MetricVisComponent } from '../components/metric_vis_controller';
|
||||
|
||||
describe('metric_vis - controller', function() {
|
||||
const vis = {
|
||||
params: {
|
||||
metric: {
|
||||
colorSchema: 'Green to Red',
|
||||
colorsRange: [{ from: 0, to: 1000 }],
|
||||
style: {},
|
||||
},
|
||||
dimensions: {
|
||||
metrics: [{ accessor: 0 }],
|
||||
bucket: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let metricController;
|
||||
|
||||
beforeEach(() => {
|
||||
metricController = new MetricVisComponent({ vis: vis, visParams: vis.params });
|
||||
});
|
||||
|
||||
it('should set the metric label and value', function() {
|
||||
const metrics = metricController._processTableGroups({
|
||||
columns: [{ id: 'col-0', name: 'Count' }],
|
||||
rows: [{ 'col-0': 4301021 }],
|
||||
});
|
||||
|
||||
expect(metrics.length).to.be(1);
|
||||
expect(metrics[0].label).to.be('Count');
|
||||
expect(metrics[0].value).to.be('<span ng-non-bindable>4301021</span>');
|
||||
});
|
||||
|
||||
it('should support multi-value metrics', function() {
|
||||
vis.params.dimensions.metrics.push({ accessor: 1 });
|
||||
const metrics = metricController._processTableGroups({
|
||||
columns: [
|
||||
{ id: 'col-0', name: '1st percentile of bytes' },
|
||||
{ id: 'col-1', name: '99th percentile of bytes' },
|
||||
],
|
||||
rows: [{ 'col-0': 182, 'col-1': 445842.4634666484 }],
|
||||
});
|
||||
|
||||
expect(metrics.length).to.be(2);
|
||||
expect(metrics[0].label).to.be('1st percentile of bytes');
|
||||
expect(metrics[0].value).to.be('<span ng-non-bindable>182</span>');
|
||||
expect(metrics[1].label).to.be('99th percentile of bytes');
|
||||
expect(metrics[1].value).to.be('<span ng-non-bindable>445842.4634666484</span>');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,57 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`MetricVisComponent should render correct structure for single metric 1`] = `
|
||||
<div
|
||||
className="mtrVis"
|
||||
>
|
||||
<MetricVisValue
|
||||
key="0"
|
||||
metric={
|
||||
Object {
|
||||
"bgColor": undefined,
|
||||
"color": undefined,
|
||||
"label": "Count",
|
||||
"lightText": false,
|
||||
"rowIndex": 0,
|
||||
"value": "<span ng-non-bindable>4301021</span>",
|
||||
}
|
||||
}
|
||||
showLabel={true}
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`MetricVisComponent should render correct structure for multi-value metrics 1`] = `
|
||||
<div
|
||||
className="mtrVis"
|
||||
>
|
||||
<MetricVisValue
|
||||
key="0"
|
||||
metric={
|
||||
Object {
|
||||
"bgColor": undefined,
|
||||
"color": undefined,
|
||||
"label": "1st percentile of bytes",
|
||||
"lightText": false,
|
||||
"rowIndex": 0,
|
||||
"value": "<span ng-non-bindable>182</span>",
|
||||
}
|
||||
}
|
||||
showLabel={true}
|
||||
/>
|
||||
<MetricVisValue
|
||||
key="1"
|
||||
metric={
|
||||
Object {
|
||||
"bgColor": undefined,
|
||||
"color": undefined,
|
||||
"label": "99th percentile of bytes",
|
||||
"lightText": false,
|
||||
"rowIndex": 0,
|
||||
"value": "<span ng-non-bindable>445842.4634666484</span>",
|
||||
}
|
||||
}
|
||||
showLabel={true}
|
||||
/>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { MetricVisComponent } from './metric_vis_component';
|
||||
import { Vis } from '../legacy_imports';
|
||||
|
||||
jest.mock('ui/new_platform');
|
||||
|
||||
type Props = MetricVisComponent['props'];
|
||||
|
||||
const baseVisData = {
|
||||
columns: [{ id: 'col-0', name: 'Count' }],
|
||||
rows: [{ 'col-0': 4301021 }],
|
||||
} as any;
|
||||
|
||||
describe('MetricVisComponent', function() {
|
||||
const vis: Vis = {
|
||||
params: {
|
||||
metric: {
|
||||
colorSchema: 'Green to Red',
|
||||
colorsRange: [{ from: 0, to: 1000 }],
|
||||
style: {},
|
||||
labels: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
dimensions: {
|
||||
metrics: [{ accessor: 0 }],
|
||||
bucket: null,
|
||||
},
|
||||
},
|
||||
} as any;
|
||||
|
||||
const getComponent = (propOverrides: Partial<Props> = {} as Partial<Props>) => {
|
||||
const props: Props = {
|
||||
vis,
|
||||
visParams: vis.params,
|
||||
visData: baseVisData,
|
||||
renderComplete: jest.fn(),
|
||||
...propOverrides,
|
||||
};
|
||||
|
||||
return shallow(<MetricVisComponent {...props} />);
|
||||
};
|
||||
|
||||
it('should render component', () => {
|
||||
expect(getComponent().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should render correct structure for single metric', function() {
|
||||
expect(getComponent()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render correct structure for multi-value metrics', function() {
|
||||
const component = getComponent({
|
||||
visData: {
|
||||
columns: [
|
||||
{ id: 'col-0', name: '1st percentile of bytes' },
|
||||
{ id: 'col-1', name: '99th percentile of bytes' },
|
||||
],
|
||||
rows: [{ 'col-0': 182, 'col-1': 445842.4634666484 }],
|
||||
},
|
||||
visParams: {
|
||||
...vis.params,
|
||||
dimensions: {
|
||||
...vis.params.dimensions,
|
||||
metrics: [{ accessor: 0 }, { accessor: 1 }],
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -19,20 +19,33 @@
|
|||
|
||||
import { last, findIndex, isNaN } from 'lodash';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { isColorDark } from '@elastic/eui';
|
||||
import { getHeatmapColors } from 'ui/color_maps';
|
||||
import { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities';
|
||||
|
||||
import { getHeatmapColors, getFormat, Vis } from '../legacy_imports';
|
||||
import { MetricVisValue } from './metric_vis_value';
|
||||
import { FieldFormat, ContentType } from '../../../../../plugins/data/public';
|
||||
import { Context } from '../metric_vis_fn';
|
||||
import { KibanaDatatable } from '../../../../../plugins/expressions/public';
|
||||
import { VisParams, MetricVisMetric } from '../types';
|
||||
import { SchemaConfig } from '../../../visualizations/public';
|
||||
|
||||
export class MetricVisComponent extends Component {
|
||||
_getLabels() {
|
||||
interface MetricVisComponentProps {
|
||||
visParams: VisParams;
|
||||
visData: Context;
|
||||
vis: Vis;
|
||||
renderComplete: () => void;
|
||||
}
|
||||
|
||||
export class MetricVisComponent extends Component<MetricVisComponentProps> {
|
||||
private getLabels() {
|
||||
const config = this.props.visParams.metric;
|
||||
const isPercentageMode = config.percentageMode;
|
||||
const colorsRange = config.colorsRange;
|
||||
const max = last(colorsRange).to;
|
||||
const labels = [];
|
||||
colorsRange.forEach(range => {
|
||||
const labels: string[] = [];
|
||||
|
||||
colorsRange.forEach((range: any) => {
|
||||
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}`);
|
||||
|
@ -41,13 +54,13 @@ export class MetricVisComponent extends Component {
|
|||
return labels;
|
||||
}
|
||||
|
||||
_getColors() {
|
||||
private getColors() {
|
||||
const config = this.props.visParams.metric;
|
||||
const invertColors = config.invertColors;
|
||||
const colorSchema = config.colorSchema;
|
||||
const colorsRange = config.colorsRange;
|
||||
const labels = this._getLabels();
|
||||
const colors = {};
|
||||
const labels = this.getLabels();
|
||||
const colors: any = {};
|
||||
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;
|
||||
|
@ -56,9 +69,9 @@ export class MetricVisComponent extends Component {
|
|||
return colors;
|
||||
}
|
||||
|
||||
_getBucket(val) {
|
||||
private getBucket(val: number) {
|
||||
const config = this.props.visParams.metric;
|
||||
let bucket = findIndex(config.colorsRange, range => {
|
||||
let bucket = findIndex(config.colorsRange, (range: any) => {
|
||||
return range.from <= val && range.to > val;
|
||||
});
|
||||
|
||||
|
@ -70,59 +83,65 @@ export class MetricVisComponent extends Component {
|
|||
return bucket;
|
||||
}
|
||||
|
||||
_getColor(val, labels, colors) {
|
||||
const bucket = this._getBucket(val);
|
||||
private getColor(val: number, labels: string[], colors: { [label: string]: string }) {
|
||||
const bucket = this.getBucket(val);
|
||||
const label = labels[bucket];
|
||||
return colors[label];
|
||||
}
|
||||
|
||||
_needsLightText(bgColor) {
|
||||
const color = /rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/.exec(bgColor);
|
||||
if (!color) {
|
||||
private needsLightText(bgColor: string) {
|
||||
const colors = /rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/.exec(bgColor);
|
||||
if (!colors) {
|
||||
return false;
|
||||
}
|
||||
return isColorDark(parseInt(color[1]), parseInt(color[2]), parseInt(color[3]));
|
||||
|
||||
const [red, green, blue] = colors.slice(1).map(parseInt);
|
||||
return isColorDark(red, green, blue);
|
||||
}
|
||||
|
||||
_getFormattedValue = (fieldFormatter, value, format = 'text') => {
|
||||
private getFormattedValue = (
|
||||
fieldFormatter: FieldFormat,
|
||||
value: any,
|
||||
format: ContentType = 'text'
|
||||
) => {
|
||||
if (isNaN(value)) return '-';
|
||||
return fieldFormatter.convert(value, format);
|
||||
};
|
||||
|
||||
_processTableGroups(table) {
|
||||
private processTableGroups(table: KibanaDatatable) {
|
||||
const config = this.props.visParams.metric;
|
||||
const dimensions = this.props.visParams.dimensions;
|
||||
const isPercentageMode = config.percentageMode;
|
||||
const min = config.colorsRange[0].from;
|
||||
const max = last(config.colorsRange).to;
|
||||
const colors = this._getColors();
|
||||
const labels = this._getLabels();
|
||||
const metrics = [];
|
||||
const colors = this.getColors();
|
||||
const labels = this.getLabels();
|
||||
const metrics: MetricVisMetric[] = [];
|
||||
|
||||
let bucketColumnId;
|
||||
let bucketFormatter;
|
||||
let bucketColumnId: string;
|
||||
let bucketFormatter: FieldFormat;
|
||||
|
||||
if (dimensions.bucket) {
|
||||
bucketColumnId = table.columns[dimensions.bucket.accessor].id;
|
||||
bucketFormatter = getFormat(dimensions.bucket.format);
|
||||
}
|
||||
|
||||
dimensions.metrics.forEach(metric => {
|
||||
dimensions.metrics.forEach((metric: SchemaConfig) => {
|
||||
const columnIndex = metric.accessor;
|
||||
const column = table.columns[columnIndex];
|
||||
const column = table?.columns[columnIndex];
|
||||
const formatter = getFormat(metric.format);
|
||||
table.rows.forEach((row, rowIndex) => {
|
||||
let title = column.name;
|
||||
let value = row[column.id];
|
||||
const color = this._getColor(value, labels, colors);
|
||||
let value: any = row[column.id];
|
||||
const color = this.getColor(value, labels, colors);
|
||||
|
||||
if (isPercentageMode) {
|
||||
value = (value - min) / (max - min);
|
||||
}
|
||||
value = this._getFormattedValue(formatter, value, 'html');
|
||||
value = this.getFormattedValue(formatter, value, 'html');
|
||||
|
||||
if (bucketColumnId) {
|
||||
const bucketValue = this._getFormattedValue(bucketFormatter, row[bucketColumnId]);
|
||||
const bucketValue = this.getFormattedValue(bucketFormatter, row[bucketColumnId]);
|
||||
title = `${bucketValue} - ${title}`;
|
||||
}
|
||||
|
||||
|
@ -130,11 +149,11 @@ export class MetricVisComponent extends Component {
|
|||
|
||||
metrics.push({
|
||||
label: title,
|
||||
value: value,
|
||||
color: shouldColor && config.style.labelColor ? color : null,
|
||||
bgColor: shouldColor && config.style.bgColor ? color : null,
|
||||
lightText: shouldColor && config.style.bgColor && this._needsLightText(color),
|
||||
rowIndex: rowIndex,
|
||||
value,
|
||||
color: shouldColor && config.style.labelColor ? color : undefined,
|
||||
bgColor: shouldColor && config.style.bgColor ? color : undefined,
|
||||
lightText: shouldColor && config.style.bgColor && this.needsLightText(color),
|
||||
rowIndex,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -142,7 +161,7 @@ export class MetricVisComponent extends Component {
|
|||
return metrics;
|
||||
}
|
||||
|
||||
_filterBucket = metric => {
|
||||
private filterBucket = (metric: MetricVisMetric) => {
|
||||
const dimensions = this.props.visParams.dimensions;
|
||||
if (!dimensions.bucket) {
|
||||
return;
|
||||
|
@ -155,27 +174,18 @@ export class MetricVisComponent extends Component {
|
|||
});
|
||||
};
|
||||
|
||||
_renderMetric = (metric, index) => {
|
||||
private renderMetric = (metric: MetricVisMetric, index: number) => {
|
||||
return (
|
||||
<MetricVisValue
|
||||
key={index}
|
||||
metric={metric}
|
||||
fontSize={this.props.visParams.metric.style.fontSize}
|
||||
onFilter={this.props.visParams.dimensions.bucket ? this._filterBucket : null}
|
||||
onFilter={this.props.visParams.dimensions.bucket ? this.filterBucket : undefined}
|
||||
showLabel={this.props.visParams.metric.labels.show}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
let metricsHtml;
|
||||
if (this.props.visData) {
|
||||
const metrics = this._processTableGroups(this.props.visData);
|
||||
metricsHtml = metrics.map(this._renderMetric);
|
||||
}
|
||||
return <div className="mtrVis">{metricsHtml}</div>;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.renderComplete();
|
||||
}
|
||||
|
@ -183,4 +193,13 @@ export class MetricVisComponent extends Component {
|
|||
componentDidUpdate() {
|
||||
this.props.renderComplete();
|
||||
}
|
||||
|
||||
render() {
|
||||
let metricsHtml;
|
||||
if (this.props.visData) {
|
||||
const metrics = this.processTableGroups(this.props.visData);
|
||||
metricsHtml = metrics.map(this.renderMetric);
|
||||
}
|
||||
return <div className="mtrVis">{metricsHtml}</div>;
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { VisOptionsProps } from 'ui/vis/editors/default';
|
||||
import { VisOptionsProps } from '../legacy_imports';
|
||||
import {
|
||||
ColorRanges,
|
||||
ColorSchemaOptions,
|
||||
|
@ -39,6 +39,7 @@ import {
|
|||
} from '../../../vis_type_vislib/public/components';
|
||||
import { ColorModes } from '../../../vis_type_vislib/public/utils/collections';
|
||||
import { MetricVisParam, VisParams } from '../types';
|
||||
import { SetColorRangeValue } from '../../../vis_type_vislib/public/components/common/color_ranges';
|
||||
|
||||
function MetricVisOptions({
|
||||
stateParams,
|
||||
|
@ -135,7 +136,7 @@ function MetricVisOptions({
|
|||
<ColorRanges
|
||||
data-test-subj="metricColorRange"
|
||||
colorsRange={stateParams.metric.colorsRange}
|
||||
setValue={setMetricValue}
|
||||
setValue={setMetricValue as SetColorRangeValue}
|
||||
setTouched={setTouched}
|
||||
setValidity={setValidity}
|
||||
/>
|
||||
|
|
|
@ -21,34 +21,32 @@ import { shallow } from 'enzyme';
|
|||
|
||||
import { MetricVisValue } from './metric_vis_value';
|
||||
|
||||
const baseMetric = { label: 'Foo', value: 'foo' } as any;
|
||||
|
||||
describe('MetricVisValue', () => {
|
||||
it('should be wrapped in EuiKeyboardAccessible if having a click listener', () => {
|
||||
const component = shallow(
|
||||
<MetricVisValue fontSize={12} metric={{ label: 'Foo', value: 'foo' }} onFilter={() => {}} />
|
||||
<MetricVisValue fontSize={12} metric={baseMetric} onFilter={() => {}} />
|
||||
);
|
||||
expect(component.find('EuiKeyboardAccessible').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should not be wrapped in EuiKeyboardAccessible without having a click listener', () => {
|
||||
const component = shallow(
|
||||
<MetricVisValue fontSize={12} metric={{ label: 'Foo', value: 'foo' }} />
|
||||
);
|
||||
const component = shallow(<MetricVisValue fontSize={12} metric={baseMetric} />);
|
||||
expect(component.find('EuiKeyboardAccessible').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('should add -isfilterable class if onFilter is provided', () => {
|
||||
const onFilter = jest.fn();
|
||||
const component = shallow(
|
||||
<MetricVisValue fontSize={12} metric={{ label: 'Foo', value: 'foo' }} onFilter={onFilter} />
|
||||
<MetricVisValue fontSize={12} metric={baseMetric} onFilter={onFilter} />
|
||||
);
|
||||
component.simulate('click');
|
||||
expect(component.find('.mtrVis__container-isfilterable')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should not add -isfilterable class if onFilter is not provided', () => {
|
||||
const component = shallow(
|
||||
<MetricVisValue fontSize={12} metric={{ label: 'Foo', value: 'foo' }} onFilter={null} />
|
||||
);
|
||||
const component = shallow(<MetricVisValue fontSize={12} metric={baseMetric} />);
|
||||
component.simulate('click');
|
||||
expect(component.find('.mtrVis__container-isfilterable')).toHaveLength(0);
|
||||
});
|
||||
|
@ -56,9 +54,9 @@ describe('MetricVisValue', () => {
|
|||
it('should call onFilter callback if provided', () => {
|
||||
const onFilter = jest.fn();
|
||||
const component = shallow(
|
||||
<MetricVisValue fontSize={12} metric={{ label: 'Foo', value: 'foo' }} onFilter={onFilter} />
|
||||
<MetricVisValue fontSize={12} metric={baseMetric} onFilter={onFilter} />
|
||||
);
|
||||
component.find('.mtrVis__container-isfilterable').simulate('click');
|
||||
expect(onFilter).toHaveBeenCalledWith({ label: 'Foo', value: 'foo' });
|
||||
expect(onFilter).toHaveBeenCalledWith(baseMetric);
|
||||
});
|
||||
});
|
|
@ -17,26 +17,36 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component, KeyboardEvent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { EuiKeyboardAccessible, keyCodes } from '@elastic/eui';
|
||||
|
||||
class MetricVisValue extends Component {
|
||||
import { MetricVisMetric } from '../types';
|
||||
|
||||
interface MetricVisValueProps {
|
||||
metric: MetricVisMetric;
|
||||
fontSize: number;
|
||||
onFilter?: (metric: MetricVisMetric) => void;
|
||||
showLabel?: boolean;
|
||||
}
|
||||
|
||||
export class MetricVisValue extends Component<MetricVisValueProps> {
|
||||
onClick = () => {
|
||||
if (this.props.onFilter) {
|
||||
this.props.onFilter(this.props.metric);
|
||||
}
|
||||
};
|
||||
|
||||
onKeyPress = e => {
|
||||
if (e.keyCode === keyCodes.ENTER) {
|
||||
onKeyPress = (event: KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.keyCode === keyCodes.ENTER) {
|
||||
this.onClick();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { fontSize, metric, onFilter, showLabel } = this.props;
|
||||
const hasFilter = !!onFilter;
|
||||
const hasFilter = Boolean(onFilter);
|
||||
|
||||
const metricValueStyle = {
|
||||
fontSize: `${fontSize}pt`,
|
||||
|
@ -52,10 +62,10 @@ class MetricVisValue extends Component {
|
|||
<div
|
||||
className={containerClassName}
|
||||
style={{ backgroundColor: metric.bgColor }}
|
||||
onClick={hasFilter ? this.onClick : null}
|
||||
onKeyPress={hasFilter ? this.onKeyPress : null}
|
||||
tabIndex={hasFilter ? 0 : null}
|
||||
role={hasFilter ? 'button' : null}
|
||||
onClick={hasFilter ? this.onClick : undefined}
|
||||
onKeyPress={hasFilter ? this.onKeyPress : undefined}
|
||||
tabIndex={hasFilter ? 0 : undefined}
|
||||
role={hasFilter ? 'button' : undefined}
|
||||
>
|
||||
<div
|
||||
className="mtrVis__value"
|
||||
|
@ -81,12 +91,3 @@ class MetricVisValue extends Component {
|
|||
return metricComponent;
|
||||
}
|
||||
}
|
||||
|
||||
MetricVisValue.propTypes = {
|
||||
fontSize: PropTypes.number.isRequired,
|
||||
metric: PropTypes.object.isRequired,
|
||||
onFilter: PropTypes.func,
|
||||
showLabel: PropTypes.bool,
|
||||
};
|
||||
|
||||
export { MetricVisValue };
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export { Vis, VisParams } from 'ui/vis';
|
||||
export { vislibColorMaps, colorSchemas, ColorSchemas } from 'ui/color_maps';
|
||||
export { getHeatmapColors } from 'ui/color_maps';
|
||||
export { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities';
|
||||
export { VisOptionsProps } from 'ui/vis/editors/default';
|
||||
// @ts-ignore
|
||||
export { Schemas } from 'ui/vis/editors/default/schemas';
|
||||
export { AggGroupNames } from 'ui/vis/editors/default';
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { vislibColorMaps, ColorSchemas } from 'ui/color_maps';
|
||||
import { vislibColorMaps, ColorSchemas } from './legacy_imports';
|
||||
import {
|
||||
ExpressionFunction,
|
||||
KibanaDatatable,
|
||||
|
@ -30,13 +30,13 @@ import {
|
|||
import { ColorModes } from '../../vis_type_vislib/public/utils/collections';
|
||||
import { visType, DimensionsVisParam, VisParams } from './types';
|
||||
|
||||
type Context = KibanaDatatable;
|
||||
export type Context = KibanaDatatable;
|
||||
|
||||
const name = 'metricVis';
|
||||
|
||||
interface Arguments {
|
||||
percentage: boolean;
|
||||
colorScheme: ColorSchemas;
|
||||
percentageMode: boolean;
|
||||
colorSchema: ColorSchemas;
|
||||
colorMode: ColorModes;
|
||||
useRanges: boolean;
|
||||
invertColors: boolean;
|
||||
|
@ -73,19 +73,19 @@ export const createMetricVisFn = (): ExpressionFunction<
|
|||
defaultMessage: 'Metric visualization',
|
||||
}),
|
||||
args: {
|
||||
percentage: {
|
||||
percentageMode: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: i18n.translate('visTypeMetric.function.percentage.help', {
|
||||
help: i18n.translate('visTypeMetric.function.percentageMode.help', {
|
||||
defaultMessage: 'Shows metric in percentage mode. Requires colorRange to be set.',
|
||||
}),
|
||||
},
|
||||
colorScheme: {
|
||||
colorSchema: {
|
||||
types: ['string'],
|
||||
default: '"Green to Red"',
|
||||
options: Object.values(vislibColorMaps).map((value: any) => value.id),
|
||||
help: i18n.translate('visTypeMetric.function.colorScheme.help', {
|
||||
defaultMessage: 'Color scheme to use',
|
||||
help: i18n.translate('visTypeMetric.function.colorSchema.help', {
|
||||
defaultMessage: 'Color schema to use',
|
||||
}),
|
||||
},
|
||||
colorMode: {
|
||||
|
@ -174,8 +174,8 @@ export const createMetricVisFn = (): ExpressionFunction<
|
|||
dimensions.bucket = args.bucket;
|
||||
}
|
||||
|
||||
if (args.percentage && (!args.colorRange || args.colorRange.length === 0)) {
|
||||
throw new Error('colorRange must be provided when using percentage');
|
||||
if (args.percentageMode && (!args.colorRange || args.colorRange.length === 0)) {
|
||||
throw new Error('colorRange must be provided when using percentageMode');
|
||||
}
|
||||
|
||||
const fontSize = Number.parseInt(args.font.spec.fontSize || '', 10);
|
||||
|
@ -188,9 +188,9 @@ export const createMetricVisFn = (): ExpressionFunction<
|
|||
visType,
|
||||
visConfig: {
|
||||
metric: {
|
||||
percentageMode: args.percentage,
|
||||
percentageMode: args.percentageMode,
|
||||
useRanges: args.useRanges,
|
||||
colorSchema: args.colorScheme,
|
||||
colorSchema: args.colorSchema,
|
||||
metricColorMode: args.colorMode,
|
||||
colorsRange: args.colorRange,
|
||||
labels: {
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
import { npStart } from 'ui/new_platform';
|
||||
// @ts-ignore
|
||||
import getStubIndexPattern from 'fixtures/stubbed_logstash_index_pattern';
|
||||
|
||||
import { Vis } from '../../visualizations/public';
|
||||
import { UrlFormat } from '../../../../plugins/data/public';
|
||||
import {
|
||||
setup as visualizationsSetup,
|
||||
start as visualizationsStart,
|
||||
} from '../../visualizations/public/np_ready/public/legacy';
|
||||
import { metricVisTypeDefinition } from './metric_vis_type';
|
||||
|
||||
jest.mock('ui/new_platform');
|
||||
|
||||
describe('metric_vis - createMetricVisTypeDefinition', () => {
|
||||
let vis: Vis;
|
||||
|
||||
beforeAll(() => {
|
||||
visualizationsSetup.types.createReactVisualization(metricVisTypeDefinition);
|
||||
(npStart.plugins.data.fieldFormats.getType as jest.Mock).mockImplementation(() => {
|
||||
return UrlFormat;
|
||||
});
|
||||
});
|
||||
|
||||
const setup = () => {
|
||||
const stubIndexPattern = getStubIndexPattern();
|
||||
|
||||
stubIndexPattern.stubSetFieldFormat('ip', 'url', {
|
||||
urlTemplate: 'http://ip.info?address={{value}}',
|
||||
labelTemplate: 'ip[{{value}}]',
|
||||
});
|
||||
|
||||
// TODO: remove when Vis is converted to typescript. Only importing Vis as type
|
||||
// @ts-ignore
|
||||
vis = new Vis(stubIndexPattern, {
|
||||
type: 'metric',
|
||||
aggs: [{ id: '1', type: 'top_hits', schema: 'metric', params: { field: 'ip' } }],
|
||||
});
|
||||
|
||||
vis.params.dimensions = {
|
||||
metrics: [
|
||||
{
|
||||
accessor: 0,
|
||||
format: {
|
||||
id: 'url',
|
||||
params: {
|
||||
urlTemplate: 'http://ip.info?address={{value}}',
|
||||
labelTemplate: 'ip[{{value}}]',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const el = document.createElement('div');
|
||||
const metricVisType = visualizationsStart.types.get('metric');
|
||||
const Controller = metricVisType.visualization;
|
||||
const controller = new Controller(el, vis);
|
||||
const render = (esResponse: any) => {
|
||||
controller.render(esResponse, vis.params);
|
||||
};
|
||||
|
||||
return { el, render };
|
||||
};
|
||||
|
||||
it('renders html value from field formatter', () => {
|
||||
const { el, render } = setup();
|
||||
|
||||
const ip = '235.195.237.208';
|
||||
render({
|
||||
columns: [{ id: 'col-0', name: 'ip' }],
|
||||
rows: [{ 'col-0': ip }],
|
||||
});
|
||||
|
||||
const links = $(el)
|
||||
.find('a[href]')
|
||||
.filter(function() {
|
||||
// @ts-ignore
|
||||
return this.href.includes('ip.info');
|
||||
});
|
||||
|
||||
expect(links.length).toBe(1);
|
||||
expect(links.text()).toBe(`ip[${ip}]`);
|
||||
});
|
||||
});
|
|
@ -19,19 +19,12 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
// @ts-ignore
|
||||
import { Schemas } from 'ui/vis/editors/default/schemas';
|
||||
|
||||
import { AggGroupNames } from 'ui/vis/editors/default';
|
||||
import { colorSchemas, ColorSchemas } from 'ui/color_maps';
|
||||
|
||||
// @ts-ignore
|
||||
import { MetricVisComponent } from './components/metric_vis_controller';
|
||||
|
||||
import { MetricVisComponent } from './components/metric_vis_component';
|
||||
import { MetricVisOptions } from './components/metric_vis_options';
|
||||
import { Schemas, AggGroupNames, colorSchemas, ColorSchemas } from './legacy_imports';
|
||||
import { ColorModes } from '../../vis_type_vislib/public/utils/collections';
|
||||
|
||||
export const metricVisDefinition = {
|
||||
export const metricVisTypeDefinition = {
|
||||
name: 'metric',
|
||||
title: i18n.translate('visTypeMetric.metricTitle', { defaultMessage: 'Metric' }),
|
||||
icon: 'visMetric',
|
||||
|
|
|
@ -22,7 +22,7 @@ import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressio
|
|||
import { VisualizationsSetup } from '../../visualizations/public';
|
||||
|
||||
import { createMetricVisFn } from './metric_vis_fn';
|
||||
import { metricVisDefinition } from './metric_vis_type';
|
||||
import { metricVisTypeDefinition } from './metric_vis_type';
|
||||
|
||||
/** @internal */
|
||||
export interface MetricVisPluginSetupDependencies {
|
||||
|
@ -40,7 +40,7 @@ export class MetricVisPlugin implements Plugin<void, void> {
|
|||
|
||||
public setup(core: CoreSetup, { expressions, visualizations }: MetricVisPluginSetupDependencies) {
|
||||
expressions.registerFunction(createMetricVisFn);
|
||||
visualizations.types.createReactVisualization(metricVisDefinition);
|
||||
visualizations.types.createReactVisualization(metricVisTypeDefinition);
|
||||
}
|
||||
|
||||
public start(core: CoreStart) {
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { ColorSchemas } from 'ui/color_maps';
|
||||
import { RangeValues } from 'ui/vis/editors/default/controls/ranges';
|
||||
import { ColorSchemas } from './legacy_imports';
|
||||
import { Range } from '../../../../plugins/expressions/public';
|
||||
import { SchemaConfig } from '../../visualizations/public';
|
||||
import { ColorModes } from '../../vis_type_vislib/public/utils/collections';
|
||||
import { Labels, Style } from '../../vis_type_vislib/public/types';
|
||||
|
@ -27,7 +27,7 @@ export const visType = 'metric';
|
|||
|
||||
export interface DimensionsVisParam {
|
||||
metrics: SchemaConfig[];
|
||||
bucket?: SchemaConfig[];
|
||||
bucket?: SchemaConfig;
|
||||
}
|
||||
|
||||
export interface MetricVisParam {
|
||||
|
@ -35,7 +35,7 @@ export interface MetricVisParam {
|
|||
useRanges: boolean;
|
||||
colorSchema: ColorSchemas;
|
||||
metricColorMode: ColorModes;
|
||||
colorsRange: RangeValues[];
|
||||
colorsRange: Range[];
|
||||
labels: Labels;
|
||||
invertColors: boolean;
|
||||
style: Style;
|
||||
|
@ -48,3 +48,12 @@ export interface VisParams {
|
|||
metric: MetricVisParam;
|
||||
type: typeof visType;
|
||||
}
|
||||
|
||||
export interface MetricVisMetric {
|
||||
value: any;
|
||||
label: string;
|
||||
color?: string;
|
||||
bgColor?: string;
|
||||
lightText: boolean;
|
||||
rowIndex: number;
|
||||
}
|
||||
|
|
|
@ -24,10 +24,12 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import { RangeValues, RangesParamEditor } from '../../legacy_imports';
|
||||
|
||||
export type SetColorRangeValue = (paramName: string, value: RangeValues[]) => void;
|
||||
|
||||
interface ColorRangesProps {
|
||||
'data-test-subj'?: string;
|
||||
colorsRange: RangeValues[];
|
||||
setValue(paramName: string, value: RangeValues[]): void;
|
||||
setValue: SetColorRangeValue;
|
||||
setValidity?(isValid: boolean): void;
|
||||
setTouched?(isTouched: boolean): void;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import { ColorRanges, ColorSchemaOptions, SwitchOption } from '../../common';
|
|||
import { GaugeOptionsInternalProps } from '.';
|
||||
import { ColorSchemaVislibParams } from '../../../types';
|
||||
import { Gauge } from '../../../gauge';
|
||||
import { SetColorRangeValue } from '../../common/color_ranges';
|
||||
|
||||
function RangesPanel({
|
||||
setGaugeValue,
|
||||
|
@ -68,7 +69,7 @@ function RangesPanel({
|
|||
<ColorRanges
|
||||
data-test-subj="gaugeColorRange"
|
||||
colorsRange={stateParams.gauge.colorsRange}
|
||||
setValue={setGaugeValue}
|
||||
setValue={setGaugeValue as SetColorRangeValue}
|
||||
setTouched={setTouched}
|
||||
setValidity={setValidity}
|
||||
/>
|
||||
|
|
|
@ -36,6 +36,7 @@ import { SetColorSchemaOptionsValue } from '../../common/color_schema';
|
|||
import { HeatmapVisParams } from '../../../heatmap';
|
||||
import { ValueAxis } from '../../../types';
|
||||
import { LabelsPanel } from './labels_panel';
|
||||
import { SetColorRangeValue } from '../../common/color_ranges';
|
||||
|
||||
function HeatmapOptions(props: VisOptionsProps<HeatmapVisParams>) {
|
||||
const { stateParams, vis, uiState, setValue, setValidity, setTouched } = props;
|
||||
|
@ -171,7 +172,7 @@ function HeatmapOptions(props: VisOptionsProps<HeatmapVisParams>) {
|
|||
<ColorRanges
|
||||
data-test-subj="heatmapColorRange"
|
||||
colorsRange={stateParams.colorsRange}
|
||||
setValue={setValue}
|
||||
setValue={setValue as SetColorRangeValue}
|
||||
setTouched={setTouched}
|
||||
setValidity={setIsColorRangesValid}
|
||||
/>
|
||||
|
|
|
@ -23,3 +23,5 @@ import { KbnVislibVisTypesPlugin as Plugin } from './plugin';
|
|||
export function plugin(initializerContext: PluginInitializerContext) {
|
||||
return new Plugin(initializerContext);
|
||||
}
|
||||
|
||||
export { ColorModes } from './utils/collections';
|
||||
|
|
|
@ -8,7 +8,7 @@ exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunct
|
|||
|
||||
exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles metric function with buckets 1`] = `"metricvis metric={visdimension 0 } metric={visdimension 1 } "`;
|
||||
|
||||
exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles metric function with percentage mode should have percentage format 1`] = `"metricvis percentage=true metric={visdimension 0 format='percent' } "`;
|
||||
exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles metric function with percentage mode should have percentage format 1`] = `"metricvis percentageMode=true metric={visdimension 0 format='percent' } "`;
|
||||
|
||||
exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles metric function without buckets 1`] = `"metricvis metric={visdimension 0 } metric={visdimension 1 } "`;
|
||||
|
||||
|
|
|
@ -305,8 +305,8 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = {
|
|||
}
|
||||
|
||||
let expr = `metricvis `;
|
||||
expr += prepareValue('percentage', percentageMode);
|
||||
expr += prepareValue('colorScheme', colorSchema);
|
||||
expr += prepareValue('percentageMode', percentageMode);
|
||||
expr += prepareValue('colorSchema', colorSchema);
|
||||
expr += prepareValue('colorMode', metricColorMode);
|
||||
expr += prepareValue('useRanges', useRanges);
|
||||
expr += prepareValue('invertColors', invertColors);
|
||||
|
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
@ -81,11 +81,15 @@ export default function({
|
|||
).toMatchScreenshot();
|
||||
});
|
||||
|
||||
it('with percentage option', async () => {
|
||||
it('with percentageMode option', async () => {
|
||||
const expression =
|
||||
'metricVis metric={visdimension 0} percentage=true colorRange={range from=0 to=1000}';
|
||||
'metricVis metric={visdimension 0} percentageMode=true colorRange={range from=0 to=1000}';
|
||||
await (
|
||||
await expectExpression('metric_percentage', expression, dataContext).toMatchSnapshot()
|
||||
await expectExpression(
|
||||
'metric_percentage_mode',
|
||||
expression,
|
||||
dataContext
|
||||
).toMatchSnapshot()
|
||||
).toMatchScreenshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
],
|
||||
"test_utils/*": [
|
||||
"src/test_utils/public/*"
|
||||
]
|
||||
],
|
||||
"fixtures/*": ["src/fixtures/*"]
|
||||
},
|
||||
// Support .tsx files and transform JSX into calls to React.createElement
|
||||
"jsx": "react",
|
||||
|
|
|
@ -17,6 +17,7 @@ export function createJestConfig({ kibanaDirectory, xPackKibanaDirectory }) {
|
|||
moduleFileExtensions: ['js', 'json', 'ts', 'tsx'],
|
||||
moduleNameMapper: {
|
||||
'^ui/(.*)': `${kibanaDirectory}/src/legacy/ui/public/$1`,
|
||||
'^fixtures/(.*)': `${kibanaDirectory}/src/fixtures/$1`,
|
||||
'uiExports/(.*)': fileMockPath,
|
||||
'^src/core/(.*)': `${kibanaDirectory}/src/core/$1`,
|
||||
'^src/legacy/(.*)': `${kibanaDirectory}/src/legacy/$1`,
|
||||
|
|
|
@ -3141,12 +3141,12 @@
|
|||
"visTypeMetric.function.bucket.help": "バケットディメンションの構成です。",
|
||||
"visTypeMetric.function.colorMode.help": "色を変更するメトリックの部分",
|
||||
"visTypeMetric.function.colorRange.help": "別の色が適用される値のグループを指定する範囲オブジェクト。",
|
||||
"visTypeMetric.function.colorScheme.help": "使用する配色",
|
||||
"visTypeMetric.function.colorSchema.help": "使用する配色",
|
||||
"visTypeMetric.function.font.help": "フォント設定です。",
|
||||
"visTypeMetric.function.help": "メトリックビジュアライゼーション",
|
||||
"visTypeMetric.function.invertColors.help": "色範囲を反転します",
|
||||
"visTypeMetric.function.metric.help": "メトリックディメンションの構成です。",
|
||||
"visTypeMetric.function.percentage.help": "パーセンテージモードでメトリックを表示します。colorRange を設定する必要があります。",
|
||||
"visTypeMetric.function.percentageMode.help": "パーセンテージモードでメトリックを表示します。colorRange を設定する必要があります。",
|
||||
"visTypeMetric.function.showLabels.help": "メトリック値の下にラベルを表示します。",
|
||||
"visTypeMetric.function.subText.help": "メトリックの下に表示するカスタムテキスト",
|
||||
"visTypeMetric.function.useRanges.help": "有効な色範囲です。",
|
||||
|
|
|
@ -3141,12 +3141,12 @@
|
|||
"visTypeMetric.function.bucket.help": "存储桶维度配置",
|
||||
"visTypeMetric.function.colorMode.help": "指标的哪部分要上色",
|
||||
"visTypeMetric.function.colorRange.help": "指定应将不同颜色应用到的值组的范围对象。",
|
||||
"visTypeMetric.function.colorScheme.help": "要使用的颜色方案",
|
||||
"visTypeMetric.function.colorSchema.help": "要使用的颜色方案",
|
||||
"visTypeMetric.function.font.help": "字体设置。",
|
||||
"visTypeMetric.function.help": "指标可视化",
|
||||
"visTypeMetric.function.invertColors.help": "反转颜色范围",
|
||||
"visTypeMetric.function.metric.help": "指标维度配置",
|
||||
"visTypeMetric.function.percentage.help": "以百分比模式显示指标。需要设置 colorRange。",
|
||||
"visTypeMetric.function.percentageMode.help": "以百分比模式显示指标。需要设置 colorRange。",
|
||||
"visTypeMetric.function.showLabels.help": "在指标值下显示标签。",
|
||||
"visTypeMetric.function.subText.help": "要在指标下显示的定制文本",
|
||||
"visTypeMetric.function.useRanges.help": "已启用颜色范围。",
|
||||
|
|
|
@ -38,7 +38,8 @@
|
|||
"monitoring/common/*": [
|
||||
"x-pack/monitoring/common/*"
|
||||
],
|
||||
"plugins/*": ["src/legacy/core_plugins/*/public/"]
|
||||
"plugins/*": ["src/legacy/core_plugins/*/public/"],
|
||||
"fixtures/*": ["src/fixtures/*"]
|
||||
},
|
||||
"types": [
|
||||
"node",
|
||||
|
|