Skip to content

Commit

Permalink
Merge branch 'master' into a11y1stBatchTests
Browse files Browse the repository at this point in the history
  • Loading branch information
elasticmachine committed Dec 12, 2019
2 parents 580d7ea + 79a8528 commit 37a0863
Show file tree
Hide file tree
Showing 10 changed files with 500 additions and 66 deletions.
22 changes: 13 additions & 9 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,19 @@
/x-pack/plugins/security/ @elastic/kibana-security
/x-pack/test/api_integration/apis/security/ @elastic/kibana-security

# Kibana Stack Services
/src/dev/i18n @elastic/kibana-stack-services
/packages/kbn-analytics/ @elastic/kibana-stack-services
/src/legacy/core_plugins/ui_metric/ @elastic/kibana-stack-services
/src/plugins/usage_collection/ @elastic/kibana-stack-services
/x-pack/legacy/plugins/telemetry @elastic/kibana-stack-services
/x-pack/legacy/plugins/alerting @elastic/kibana-stack-services
/x-pack/legacy/plugins/actions @elastic/kibana-stack-services
/x-pack/legacy/plugins/task_manager @elastic/kibana-stack-services
# Kibana Localization
/src/dev/i18n @elastic/kibana-localization

# Pulse
/packages/kbn-analytics/ @elastic/pulse
/src/legacy/core_plugins/ui_metric/ @elastic/pulse
/src/plugins/usage_collection/ @elastic/pulse
/x-pack/legacy/plugins/telemetry @elastic/pulse

# Kibana Alerting Services
/x-pack/legacy/plugins/alerting @elastic/kibana-alerting-services
/x-pack/legacy/plugins/actions @elastic/kibana-alerting-services
/x-pack/legacy/plugins/task_manager @elastic/kibana-alerting-services

# Design
**/*.scss @elastic/kibana-design
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* Overrides for d3/svg default styles */
.mlColorRangeLegend {
text {
@include fontSize($euiFontSizeXS - 2px);
fill: $euiColorDarkShade;
}

.axis path {
fill: none;
stroke: none;
}

.axis line {
fill: none;
stroke: $euiColorMediumShade;
shape-rendering: crispEdges;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import 'color_range_legend';
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useEffect, useRef, FC } from 'react';
import d3 from 'd3';

import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';

const COLOR_RANGE_RESOLUTION = 10;

interface ColorRangeLegendProps {
colorRange: (d: number) => string;
justifyTicks?: boolean;
showTicks?: boolean;
title?: string;
width?: number;
}

/**
* Component to render a legend for color ranges to be used for color coding
* table cells and visualizations.
*
* This current version supports normalized value ranges (0-1) only.
*
* @param props ColorRangeLegendProps
*/
export const ColorRangeLegend: FC<ColorRangeLegendProps> = ({
colorRange,
justifyTicks = false,
showTicks = true,
title,
width = 250,
}) => {
const d3Container = useRef<null | SVGSVGElement>(null);

const scale = d3.range(COLOR_RANGE_RESOLUTION + 1).map(d => ({
offset: (d / COLOR_RANGE_RESOLUTION) * 100,
stopColor: colorRange(d / COLOR_RANGE_RESOLUTION),
}));

useEffect(() => {
if (d3Container.current === null) {
return;
}

const wrapperHeight = 32;
const wrapperWidth = width;

// top: 2 — adjust vertical alignment with title text
// bottom: 20 — room for axis ticks and labels
// left/right: 1 — room for first and last axis tick
// when justifyTicks is enabled, the left margin is increased to not cut off the first tick label
const margin = { top: 2, bottom: 20, left: justifyTicks || !showTicks ? 1 : 4, right: 1 };

const legendWidth = wrapperWidth - margin.left - margin.right;
const legendHeight = wrapperHeight - margin.top - margin.bottom;

// remove, then redraw the legend
d3.select(d3Container.current)
.selectAll('*')
.remove();

const wrapper = d3
.select(d3Container.current)
.classed('mlColorRangeLegend', true)
.attr('width', wrapperWidth)
.attr('height', wrapperHeight)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

// append gradient bar
const gradient = wrapper
.append('defs')
.append('linearGradient')
.attr('id', 'mlColorRangeGradient')
.attr('x1', '0%')
.attr('y1', '0%')
.attr('x2', '100%')
.attr('y2', '0%')
.attr('spreadMethod', 'pad');

scale.forEach(function(d) {
gradient
.append('stop')
.attr('offset', `${d.offset}%`)
.attr('stop-color', d.stopColor)
.attr('stop-opacity', 1);
});

wrapper
.append('rect')
.attr('x1', 0)
.attr('y1', 0)
.attr('width', legendWidth)
.attr('height', legendHeight)
.style('fill', 'url(#mlColorRangeGradient)');

const axisScale = d3.scale
.linear()
.domain([0, 1])
.range([0, legendWidth]);

// Using this formatter ensures we get e.g. `0` and not `0.0`, but still `0.1`, `0.2` etc.
const tickFormat = d3.format('');
const legendAxis = d3.svg
.axis()
.scale(axisScale)
.orient('bottom')
.tickFormat(tickFormat)
.tickSize(legendHeight + 4)
.ticks(legendWidth / 40);

wrapper
.append('g')
.attr('class', 'legend axis')
.attr('transform', 'translate(0, 0)')
.call(legendAxis);

// Adjust the alignment of the first and last tick text
// so that the tick labels don't overflow the color range.
if (justifyTicks || !showTicks) {
const text = wrapper.selectAll('text')[0];
if (text.length > 1) {
d3.select(text[0]).style('text-anchor', 'start');
d3.select(text[text.length - 1]).style('text-anchor', 'end');
}
}

if (!showTicks) {
wrapper.selectAll('.axis line').style('display', 'none');
}
}, [JSON.stringify(scale), d3Container.current]);

if (title === undefined) {
return <svg ref={d3Container} />;
}

return (
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={false}>
<EuiText size="xs">
<strong>{title}</strong>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<svg ref={d3Container} />
</EuiFlexItem>
</EuiFlexGroup>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export { ColorRangeLegend } from './color_range_legend';
export {
colorRangeOptions,
colorRangeScaleOptions,
useColorRange,
COLOR_RANGE,
COLOR_RANGE_SCALE,
} from './use_color_range';
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { influencerColorScaleFactory } from './use_color_range';

jest.mock('../../contexts/ui/use_ui_chrome_context');

describe('useColorRange', () => {
test('influencerColorScaleFactory(1)', () => {
const influencerColorScale = influencerColorScaleFactory(1);

expect(influencerColorScale(0)).toBe(0);
expect(influencerColorScale(0.1)).toBe(0.1);
expect(influencerColorScale(0.2)).toBe(0.2);
expect(influencerColorScale(0.3)).toBe(0.3);
expect(influencerColorScale(0.4)).toBe(0.4);
expect(influencerColorScale(0.5)).toBe(0.5);
expect(influencerColorScale(0.6)).toBe(0.6);
expect(influencerColorScale(0.7)).toBe(0.7);
expect(influencerColorScale(0.8)).toBe(0.8);
expect(influencerColorScale(0.9)).toBe(0.9);
expect(influencerColorScale(1)).toBe(1);
});

test('influencerColorScaleFactory(2)', () => {
const influencerColorScale = influencerColorScaleFactory(2);

expect(influencerColorScale(0)).toBe(0);
expect(influencerColorScale(0.1)).toBe(0);
expect(influencerColorScale(0.2)).toBe(0);
expect(influencerColorScale(0.3)).toBe(0);
expect(influencerColorScale(0.4)).toBe(0);
expect(influencerColorScale(0.5)).toBe(0);
expect(influencerColorScale(0.6)).toBe(0.04999999999999999);
expect(influencerColorScale(0.7)).toBe(0.09999999999999998);
expect(influencerColorScale(0.8)).toBe(0.15000000000000002);
expect(influencerColorScale(0.9)).toBe(0.2);
expect(influencerColorScale(1)).toBe(0.25);
});

test('influencerColorScaleFactory(3)', () => {
const influencerColorScale = influencerColorScaleFactory(3);

expect(influencerColorScale(0)).toBe(0);
expect(influencerColorScale(0.1)).toBe(0);
expect(influencerColorScale(0.2)).toBe(0);
expect(influencerColorScale(0.3)).toBe(0);
expect(influencerColorScale(0.4)).toBe(0.05000000000000003);
expect(influencerColorScale(0.5)).toBe(0.125);
expect(influencerColorScale(0.6)).toBe(0.2);
expect(influencerColorScale(0.7)).toBe(0.27499999999999997);
expect(influencerColorScale(0.8)).toBe(0.35000000000000003);
expect(influencerColorScale(0.9)).toBe(0.425);
expect(influencerColorScale(1)).toBe(0.5);
});
});
Loading

0 comments on commit 37a0863

Please sign in to comment.