mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge remote-tracking branch 'grafana/master' into show-all-columns
* grafana/master: (56 commits) another change that didn't come with earlier commit change that didn't come with in last commit reversed dashboard-padding Update CloudWatch metrics/dimension list (#16102) brought back dashboard-padding and panel-padding variables, made dashboard-padding more specific fix(prometheus): Change aligment of range queries (#16110) Minor refactoring of testdata query order PR #16122 cleaner version maintain query order Update PLUGIN_DEV.md Merge with master, and updated logo and name more fixes to snapshot more fixes to snapshot removed empty space in snapshot fixed snapshot for test removed dashboard variables, removed headings-font-family variable, created theme variables for links and z-index, removed unused class in _panel_editor and _dashboard Tooltip: show percent instead of value Right tooltip position Add "No data points" message Improve tooltip look ...
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
"@torkelo/react-select": "2.1.1",
|
||||
"@types/react-color": "^2.14.0",
|
||||
"classnames": "^2.2.5",
|
||||
"d3": "^5.7.0",
|
||||
"jquery": "^3.2.1",
|
||||
"lodash": "^4.17.10",
|
||||
"moment": "^2.22.2",
|
||||
@@ -43,6 +44,7 @@
|
||||
"@storybook/addon-knobs": "^4.1.7",
|
||||
"@storybook/react": "^4.1.4",
|
||||
"@types/classnames": "^2.2.6",
|
||||
"@types/d3": "^5.7.0",
|
||||
"@types/jest": "^23.3.2",
|
||||
"@types/jquery": "^1.10.35",
|
||||
"@types/lodash": "^4.14.119",
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { number, text, object } from '@storybook/addon-knobs';
|
||||
import { PieChart, PieChartType } from './PieChart';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { renderComponentWithTheme } from '../../utils/storybook/withTheme';
|
||||
|
||||
const getKnobs = () => {
|
||||
return {
|
||||
datapoints: object('datapoints', [
|
||||
{
|
||||
value: 100,
|
||||
name: '100',
|
||||
color: '#7EB26D',
|
||||
},
|
||||
{
|
||||
value: 200,
|
||||
name: '200',
|
||||
color: '#6ED0E0',
|
||||
},
|
||||
]),
|
||||
pieType: text('pieType', PieChartType.PIE),
|
||||
strokeWidth: number('strokeWidth', 1),
|
||||
unit: text('unit', 'ms'),
|
||||
};
|
||||
};
|
||||
|
||||
const PieChartStories = storiesOf('UI/PieChart/PieChart', module);
|
||||
|
||||
PieChartStories.addDecorator(withCenteredStory);
|
||||
|
||||
PieChartStories.add('Pie type: pie', () => {
|
||||
const { datapoints, pieType, strokeWidth, unit } = getKnobs();
|
||||
|
||||
return renderComponentWithTheme(PieChart, {
|
||||
width: 200,
|
||||
height: 400,
|
||||
datapoints,
|
||||
pieType,
|
||||
strokeWidth,
|
||||
unit,
|
||||
});
|
||||
});
|
||||
147
packages/grafana-ui/src/components/PieChart/PieChart.tsx
Normal file
147
packages/grafana-ui/src/components/PieChart/PieChart.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { select, pie, arc, event } from 'd3';
|
||||
import { sum } from 'lodash';
|
||||
|
||||
import { GrafanaThemeType } from '../../types';
|
||||
import { Themeable } from '../../index';
|
||||
|
||||
export enum PieChartType {
|
||||
PIE = 'pie',
|
||||
DONUT = 'donut',
|
||||
}
|
||||
|
||||
export interface PieChartDataPoint {
|
||||
value: number;
|
||||
name: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
export interface Props extends Themeable {
|
||||
height: number;
|
||||
width: number;
|
||||
datapoints: PieChartDataPoint[];
|
||||
|
||||
unit: string;
|
||||
pieType: PieChartType;
|
||||
strokeWidth: number;
|
||||
}
|
||||
|
||||
export class PieChart extends PureComponent<Props> {
|
||||
containerElement: any;
|
||||
svgElement: any;
|
||||
tooltipElement: any;
|
||||
tooltipValueElement: any;
|
||||
|
||||
static defaultProps = {
|
||||
pieType: 'pie',
|
||||
format: 'short',
|
||||
stat: 'current',
|
||||
strokeWidth: 1,
|
||||
theme: GrafanaThemeType.Dark,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.draw();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.draw();
|
||||
}
|
||||
|
||||
draw() {
|
||||
const { datapoints, pieType, strokeWidth } = this.props;
|
||||
|
||||
if (datapoints.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = datapoints.map(datapoint => datapoint.value);
|
||||
const names = datapoints.map(datapoint => datapoint.name);
|
||||
const colors = datapoints.map(datapoint => datapoint.color);
|
||||
|
||||
const total = sum(data) || 1;
|
||||
const percents = data.map((item: number) => (item / total) * 100);
|
||||
|
||||
const width = this.containerElement.offsetWidth;
|
||||
const height = this.containerElement.offsetHeight;
|
||||
const radius = Math.min(width, height) / 2;
|
||||
|
||||
const outerRadius = radius - radius / 10;
|
||||
const innerRadius = pieType === PieChartType.PIE ? 0 : radius - radius / 3;
|
||||
|
||||
const svg = select(this.svgElement)
|
||||
.html('')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.append('g')
|
||||
.attr('transform', `translate(${width / 2},${height / 2})`);
|
||||
|
||||
const pieChart = pie();
|
||||
|
||||
const customArc = arc()
|
||||
.outerRadius(outerRadius)
|
||||
.innerRadius(innerRadius)
|
||||
.padAngle(0);
|
||||
|
||||
svg
|
||||
.selectAll('path')
|
||||
.data(pieChart(data))
|
||||
.enter()
|
||||
.append('path')
|
||||
.attr('d', customArc as any)
|
||||
.attr('fill', (d: any, idx: number) => colors[idx])
|
||||
.style('fill-opacity', 0.15)
|
||||
.style('stroke', (d: any, idx: number) => colors[idx])
|
||||
.style('stroke-width', `${strokeWidth}px`)
|
||||
.on('mouseover', (d: any, idx: any) => {
|
||||
select(this.tooltipElement).style('opacity', 1);
|
||||
select(this.tooltipValueElement).text(`${names[idx]} (${percents[idx].toFixed(2)}%)`);
|
||||
})
|
||||
.on('mousemove', () => {
|
||||
select(this.tooltipElement)
|
||||
.style('top', `${event.pageY - height / 2}px`)
|
||||
.style('left', `${event.pageX}px`);
|
||||
})
|
||||
.on('mouseout', () => {
|
||||
select(this.tooltipElement).style('opacity', 0);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { height, width, datapoints } = this.props;
|
||||
|
||||
if (datapoints.length > 0) {
|
||||
return (
|
||||
<div className="piechart-panel">
|
||||
<div
|
||||
ref={element => (this.containerElement = element)}
|
||||
className="piechart-container"
|
||||
style={{
|
||||
height: `${height * 0.9}px`,
|
||||
width: `${Math.min(width, height * 1.3)}px`,
|
||||
}}
|
||||
>
|
||||
<svg ref={element => (this.svgElement = element)} />
|
||||
</div>
|
||||
<div className="piechart-tooltip" ref={element => (this.tooltipElement = element)}>
|
||||
<div className="piechart-tooltip-time">
|
||||
<div
|
||||
id="tooltip-value"
|
||||
className="piechart-tooltip-value"
|
||||
ref={element => (this.tooltipValueElement = element)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="piechart-panel">
|
||||
<div className="datapoints-warning">
|
||||
<span className="small">No data points</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,7 +195,7 @@ exports[`Render should render with base threshold 1`] = `
|
||||
"typography": Object {
|
||||
"fontFamily": Object {
|
||||
"monospace": "Menlo, Monaco, Consolas, 'Courier New', monospace",
|
||||
"sansSerif": "'Roboto', Helvetica, Arial, sans-serif",
|
||||
"sansSerif": "'Roboto', 'Helvetica Neue', Arial, sans-serif",
|
||||
},
|
||||
"heading": Object {
|
||||
"h1": "28px",
|
||||
@@ -211,6 +211,10 @@ exports[`Render should render with base threshold 1`] = `
|
||||
"sm": 1.1,
|
||||
"xs": 1,
|
||||
},
|
||||
"link": Object {
|
||||
"decoration": "none",
|
||||
"hoverDecoration": "none",
|
||||
},
|
||||
"size": Object {
|
||||
"base": "13px",
|
||||
"lg": "18px",
|
||||
@@ -225,6 +229,15 @@ exports[`Render should render with base threshold 1`] = `
|
||||
"semibold": 500,
|
||||
},
|
||||
},
|
||||
"zIndex": Object {
|
||||
"dropdown": "1000",
|
||||
"modal": "1050",
|
||||
"modalBackdrop": "1040",
|
||||
"navbarFixed": "1020",
|
||||
"sidemenu": "1025",
|
||||
"tooltip": "1030",
|
||||
"typeahead": "1060",
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
@@ -339,7 +352,7 @@ exports[`Render should render with base threshold 1`] = `
|
||||
"typography": Object {
|
||||
"fontFamily": Object {
|
||||
"monospace": "Menlo, Monaco, Consolas, 'Courier New', monospace",
|
||||
"sansSerif": "'Roboto', Helvetica, Arial, sans-serif",
|
||||
"sansSerif": "'Roboto', 'Helvetica Neue', Arial, sans-serif",
|
||||
},
|
||||
"heading": Object {
|
||||
"h1": "28px",
|
||||
@@ -355,6 +368,10 @@ exports[`Render should render with base threshold 1`] = `
|
||||
"sm": 1.1,
|
||||
"xs": 1,
|
||||
},
|
||||
"link": Object {
|
||||
"decoration": "none",
|
||||
"hoverDecoration": "none",
|
||||
},
|
||||
"size": Object {
|
||||
"base": "13px",
|
||||
"lg": "18px",
|
||||
@@ -369,6 +386,15 @@ exports[`Render should render with base threshold 1`] = `
|
||||
"semibold": 500,
|
||||
},
|
||||
},
|
||||
"zIndex": Object {
|
||||
"dropdown": "1000",
|
||||
"modal": "1050",
|
||||
"modalBackdrop": "1040",
|
||||
"navbarFixed": "1020",
|
||||
"sidemenu": "1025",
|
||||
"tooltip": "1030",
|
||||
"typeahead": "1060",
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -25,6 +25,7 @@ export { PanelOptionsGrid } from './PanelOptionsGrid/PanelOptionsGrid';
|
||||
export { ValueMappingsEditor } from './ValueMappingsEditor/ValueMappingsEditor';
|
||||
export { Switch } from './Switch/Switch';
|
||||
export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult';
|
||||
export { PieChart, PieChartDataPoint, PieChartType } from './PieChart/PieChart';
|
||||
export { UnitPicker } from './UnitPicker/UnitPicker';
|
||||
export { Input, InputStatus } from './Input/Input';
|
||||
|
||||
|
||||
@@ -110,7 +110,6 @@ $font-size-h4: ${theme.typography.heading.h4} !default;
|
||||
$font-size-h5: ${theme.typography.heading.h5} !default;
|
||||
$font-size-h6: ${theme.typography.heading.h6} !default;
|
||||
|
||||
$headings-font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
$headings-line-height: ${theme.typography.lineHeight.sm} !default;
|
||||
|
||||
// Components
|
||||
@@ -130,8 +129,8 @@ $page-sidebar-margin: 56px;
|
||||
|
||||
// Links
|
||||
// -------------------------
|
||||
$link-decoration: none !default;
|
||||
$link-hover-decoration: none !default;
|
||||
$link-decoration: ${theme.typography.link.decoration} !default;
|
||||
$link-hover-decoration: ${theme.typography.link.hoverDecoration} !default;
|
||||
|
||||
// Tables
|
||||
//
|
||||
@@ -166,13 +165,13 @@ $form-icon-danger: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www
|
||||
// -------------------------
|
||||
// Used for a bird's eye view of components dependent on the z-axis
|
||||
// Try to avoid customizing these :)
|
||||
$zindex-dropdown: 1000;
|
||||
$zindex-navbar-fixed: 1020;
|
||||
$zindex-sidemenu: 1025;
|
||||
$zindex-tooltip: 1030;
|
||||
$zindex-modal-backdrop: 1040;
|
||||
$zindex-modal: 1050;
|
||||
$zindex-typeahead: 1060;
|
||||
$zindex-dropdown: ${theme.zIndex.dropdown};
|
||||
$zindex-navbar-fixed: ${theme.zIndex.navbarFixed};
|
||||
$zindex-sidemenu: ${theme.zIndex.sidemenu};
|
||||
$zindex-tooltip: ${theme.zIndex.tooltip};
|
||||
$zindex-modal-backdrop: ${theme.zIndex.modalBackdrop};
|
||||
$zindex-modal: ${theme.zIndex.modal};
|
||||
$zindex-typeahead: ${theme.zIndex.typeahead};
|
||||
|
||||
// Buttons
|
||||
//
|
||||
@@ -197,10 +196,8 @@ $btn-semi-transparent: rgba(0, 0, 0, 0.2) !default;
|
||||
$side-menu-width: 60px;
|
||||
|
||||
// dashboard
|
||||
$dashboard-padding: 10px * 2;
|
||||
$panel-horizontal-padding: 10;
|
||||
$panel-vertical-padding: 5;
|
||||
$panel-padding: 0px $panel-horizontal-padding + 0px $panel-vertical-padding + 0px $panel-horizontal-padding + 0px;
|
||||
$dashboard-padding: $space-md;
|
||||
$panel-padding: 0 $space-md $space-sm $space-md;
|
||||
|
||||
// tabs
|
||||
$tabs-padding: 10px 15px 9px;
|
||||
|
||||
@@ -4,7 +4,7 @@ const theme: GrafanaThemeCommons = {
|
||||
name: 'Grafana Default',
|
||||
typography: {
|
||||
fontFamily: {
|
||||
sansSerif: "'Roboto', Helvetica, Arial, sans-serif",
|
||||
sansSerif: "'Roboto', 'Helvetica Neue', Arial, sans-serif",
|
||||
monospace: "Menlo, Monaco, Consolas, 'Courier New', monospace",
|
||||
},
|
||||
size: {
|
||||
@@ -34,6 +34,10 @@ const theme: GrafanaThemeCommons = {
|
||||
md: 4 / 3,
|
||||
lg: 1.5,
|
||||
},
|
||||
link: {
|
||||
decoration: 'none',
|
||||
hoverDecoration: 'none',
|
||||
},
|
||||
},
|
||||
breakpoints: {
|
||||
xs: '0',
|
||||
@@ -66,6 +70,15 @@ const theme: GrafanaThemeCommons = {
|
||||
horizontal: 10,
|
||||
vertical: 5,
|
||||
},
|
||||
zIndex: {
|
||||
dropdown: '1000',
|
||||
navbarFixed: '1020',
|
||||
sidemenu: '1025',
|
||||
tooltip: '1030',
|
||||
modalBackdrop: '1040',
|
||||
modal: '1050',
|
||||
typeahead: '1060',
|
||||
},
|
||||
};
|
||||
|
||||
export default theme;
|
||||
|
||||
@@ -46,6 +46,10 @@ export interface GrafanaThemeCommons {
|
||||
h5: string;
|
||||
h6: string;
|
||||
};
|
||||
link: {
|
||||
decoration: string;
|
||||
hoverDecoration: string;
|
||||
};
|
||||
};
|
||||
spacing: {
|
||||
d: string;
|
||||
@@ -71,6 +75,15 @@ export interface GrafanaThemeCommons {
|
||||
horizontal: number;
|
||||
vertical: number;
|
||||
};
|
||||
zIndex: {
|
||||
dropdown: string;
|
||||
navbarFixed: string;
|
||||
sidemenu: string;
|
||||
tooltip: string;
|
||||
modalBackdrop: string;
|
||||
modal: string;
|
||||
typeahead: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface GrafanaTheme extends GrafanaThemeCommons {
|
||||
|
||||
Reference in New Issue
Block a user