mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'master' into develop
This commit is contained in:
@@ -27,7 +27,7 @@ _.move = function (array, fromIndex, toIndex) {
|
||||
return array;
|
||||
};
|
||||
|
||||
import {coreModule} from './core/core';
|
||||
import {coreModule, registerAngularDirectives} from './core/core';
|
||||
|
||||
export class GrafanaApp {
|
||||
registerFunctions: any;
|
||||
@@ -109,6 +109,9 @@ export class GrafanaApp {
|
||||
// makes it possible to add dynamic stuff
|
||||
this.useModule(coreModule);
|
||||
|
||||
// register react angular wrappers
|
||||
registerAngularDirectives();
|
||||
|
||||
var preBootRequires = [System.import('app/features/all')];
|
||||
|
||||
Promise.all(preBootRequires).then(() => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { react2AngularDirective } from 'app/core/utils/react2angular';
|
||||
import { PasswordStrength } from './ui/PasswordStrength';
|
||||
import { PasswordStrength } from './components/PasswordStrength';
|
||||
|
||||
export function registerAngularDirectives() {
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface IProps {
|
||||
onColorSelect: (c: string) => void;
|
||||
}
|
||||
|
||||
export class GfColorPalette extends React.Component<IProps, any> {
|
||||
export class ColorPalette extends React.Component<IProps, any> {
|
||||
paletteColors: string[];
|
||||
|
||||
constructor(props) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import $ from 'jquery';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { GfColorPalette } from './ColorPalette';
|
||||
import { GfSpectrumPicker } from './SpectrumPicker';
|
||||
import { ColorPalette } from './ColorPalette';
|
||||
import { SpectrumPicker } from './SpectrumPicker';
|
||||
|
||||
const DEFAULT_COLOR = '#000000';
|
||||
|
||||
@@ -82,12 +82,12 @@ export class ColorPickerPopover extends React.Component<IProps, any> {
|
||||
render() {
|
||||
const paletteTab = (
|
||||
<div id="palette">
|
||||
<GfColorPalette color={this.state.color} onColorSelect={this.sampleColorSelected.bind(this)} />
|
||||
<ColorPalette color={this.state.color} onColorSelect={this.sampleColorSelected.bind(this)} />
|
||||
</div>
|
||||
);
|
||||
const spectrumTab = (
|
||||
<div id="spectrum">
|
||||
<GfSpectrumPicker color={this.state.color} onColorSelect={this.spectrumColorSelected.bind(this)} options={{}} />
|
||||
<SpectrumPicker color={this.state.color} onColorSelect={this.spectrumColorSelected.bind(this)} options={{}} />
|
||||
</div>
|
||||
);
|
||||
const currentTab = this.state.tab === 'palette' ? paletteTab : spectrumTab;
|
||||
|
||||
@@ -9,7 +9,7 @@ export interface IProps {
|
||||
onColorSelect: (c: string) => void;
|
||||
}
|
||||
|
||||
export class GfSpectrumPicker extends React.Component<IProps, any> {
|
||||
export class SpectrumPicker extends React.Component<IProps, any> {
|
||||
elem: any;
|
||||
isMoving: boolean;
|
||||
|
||||
|
||||
@@ -53,10 +53,9 @@ import {orgSwitcher} from './components/org_switcher';
|
||||
import {profiler} from './profiler';
|
||||
import {registerAngularDirectives} from './angular_wrappers';
|
||||
|
||||
registerAngularDirectives();
|
||||
|
||||
export {
|
||||
profiler,
|
||||
registerAngularDirectives,
|
||||
arrayJoin,
|
||||
coreModule,
|
||||
grafanaAppDirective,
|
||||
|
||||
10
public/app/core/specs/ColorPalette.jest.tsx
Normal file
10
public/app/core/specs/ColorPalette.jest.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { ColorPalette } from '../components/colorpicker/ColorPalette';
|
||||
|
||||
describe('CollorPalette', () => {
|
||||
it('renders correctly', () => {
|
||||
const tree = renderer.create(<ColorPalette color="#EAB839" onColorSelect={jest.fn()} />).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
|
||||
import {PasswordStrength} from '../ui/PasswordStrength';
|
||||
import {PasswordStrength} from '../components/PasswordStrength';
|
||||
|
||||
describe('PasswordStrength', () => {
|
||||
|
||||
|
||||
628
public/app/core/specs/__snapshots__/ColorPalette.jest.tsx.snap
Normal file
628
public/app/core/specs/__snapshots__/ColorPalette.jest.tsx.snap
Normal file
@@ -0,0 +1,628 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CollorPalette renders correctly 1`] = `
|
||||
<div
|
||||
className="graph-legend-popover"
|
||||
>
|
||||
<p
|
||||
className="m-b-0"
|
||||
>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#890f02",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#58140c",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#99440a",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#c15c17",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#967302",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#cca300",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#3f6833",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#2f575e",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#64b0c8",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#052b51",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#0a50a1",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#584477",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#3f2b5b",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#511749",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#e24d42",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#bf1b00",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#ef843c",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#f4d598",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#e5ac0e",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#9ac48a",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#508642",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#6ed0e0",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#65c5db",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#0a437c",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#447ebc",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#614d93",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#d683ce",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#6d1f62",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#ea6460",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#e0752d",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#f9934e",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#fceaca",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle-o"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#eab839",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#b7dbab",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#629e51",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#70dbed",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#82b5d8",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#1f78c1",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#aea2e0",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#705da0",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#e5a8e2",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#962d82",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#f29191",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#fce2de",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#f9ba8f",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#f9e2d2",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#f2c96d",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#e0f9d7",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#7eb26d",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#cffaff",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#badff4",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#5195ce",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#dedaf7",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#806eb7",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#f9d9f9",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
<i
|
||||
className="pointer fa fa-circle"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#ba43a9",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</i>
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,5 +1,3 @@
|
||||
///<reference path="../headers/common.d.ts" />
|
||||
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import _ from 'lodash';
|
||||
|
||||
@@ -23,6 +21,7 @@ export default class TimeSeries {
|
||||
id: string;
|
||||
label: string;
|
||||
alias: string;
|
||||
aliasEscaped: string;
|
||||
color: string;
|
||||
valueFormater: any;
|
||||
stats: any;
|
||||
@@ -52,6 +51,7 @@ export default class TimeSeries {
|
||||
this.label = opts.alias;
|
||||
this.id = opts.alias;
|
||||
this.alias = opts.alias;
|
||||
this.aliasEscaped = _.escape(opts.alias);
|
||||
this.color = opts.color;
|
||||
this.valueFormater = kbn.valueFormats.none;
|
||||
this.stats = {};
|
||||
|
||||
@@ -3,6 +3,7 @@ import './editor_ctrl';
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import coreModule from 'app/core/core_module';
|
||||
import {makeRegions, dedupAnnotations} from './events_processing';
|
||||
|
||||
export class AnnotationsSrv {
|
||||
globalAnnotationsPromise: any;
|
||||
@@ -161,83 +162,4 @@ export class AnnotationsSrv {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function converts annotation events into set
|
||||
* of single events and regions (event consist of two)
|
||||
* @param annotations
|
||||
* @param options
|
||||
*/
|
||||
function makeRegions(annotations, options) {
|
||||
let [regionEvents, singleEvents] = _.partition(annotations, 'regionId');
|
||||
let regions = getRegions(regionEvents, options.range);
|
||||
annotations = _.concat(regions, singleEvents);
|
||||
return annotations;
|
||||
}
|
||||
|
||||
function getRegions(events, range) {
|
||||
let region_events = _.filter(events, event => {
|
||||
return event.regionId;
|
||||
});
|
||||
let regions = _.groupBy(region_events, 'regionId');
|
||||
regions = _.compact(
|
||||
_.map(regions, region_events => {
|
||||
let region_obj = _.head(region_events);
|
||||
if (region_events && region_events.length > 1) {
|
||||
region_obj.timeEnd = region_events[1].time;
|
||||
region_obj.isRegion = true;
|
||||
return region_obj;
|
||||
} else {
|
||||
if (region_events && region_events.length) {
|
||||
// Don't change proper region object
|
||||
if (!region_obj.time || !region_obj.timeEnd) {
|
||||
// This is cut region
|
||||
if (isStartOfRegion(region_obj)) {
|
||||
region_obj.timeEnd = range.to.valueOf() - 1;
|
||||
} else {
|
||||
// Start time = null
|
||||
region_obj.timeEnd = region_obj.time;
|
||||
region_obj.time = range.from.valueOf() + 1;
|
||||
}
|
||||
region_obj.isRegion = true;
|
||||
}
|
||||
|
||||
return region_obj;
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
function isStartOfRegion(event): boolean {
|
||||
return event.id && event.id === event.regionId;
|
||||
}
|
||||
|
||||
function dedupAnnotations(annotations) {
|
||||
let dedup = [];
|
||||
|
||||
// Split events by annotationId property existance
|
||||
let events = _.partition(annotations, 'id');
|
||||
|
||||
let eventsById = _.groupBy(events[0], 'id');
|
||||
dedup = _.map(eventsById, eventGroup => {
|
||||
if (eventGroup.length > 1 && !_.every(eventGroup, isPanelAlert)) {
|
||||
// Get first non-panel alert
|
||||
return _.find(eventGroup, event => {
|
||||
return event.eventType !== 'panel-alert';
|
||||
});
|
||||
} else {
|
||||
return _.head(eventGroup);
|
||||
}
|
||||
});
|
||||
|
||||
dedup = _.concat(dedup, events[1]);
|
||||
return dedup;
|
||||
}
|
||||
|
||||
function isPanelAlert(event) {
|
||||
return event.eventType === 'panel-alert';
|
||||
}
|
||||
|
||||
coreModule.service('annotationsSrv', AnnotationsSrv);
|
||||
|
||||
80
public/app/features/annotations/events_processing.ts
Normal file
80
public/app/features/annotations/events_processing.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
/**
|
||||
* This function converts annotation events into set
|
||||
* of single events and regions (event consist of two)
|
||||
* @param annotations
|
||||
* @param options
|
||||
*/
|
||||
export function makeRegions(annotations, options) {
|
||||
let [regionEvents, singleEvents] = _.partition(annotations, 'regionId');
|
||||
let regions = getRegions(regionEvents, options.range);
|
||||
annotations = _.concat(regions, singleEvents);
|
||||
return annotations;
|
||||
}
|
||||
|
||||
function getRegions(events, range) {
|
||||
let region_events = _.filter(events, event => {
|
||||
return event.regionId;
|
||||
});
|
||||
let regions = _.groupBy(region_events, 'regionId');
|
||||
regions = _.compact(
|
||||
_.map(regions, region_events => {
|
||||
let region_obj = _.head(region_events);
|
||||
if (region_events && region_events.length > 1) {
|
||||
region_obj.timeEnd = region_events[1].time;
|
||||
region_obj.isRegion = true;
|
||||
return region_obj;
|
||||
} else {
|
||||
if (region_events && region_events.length) {
|
||||
// Don't change proper region object
|
||||
if (!region_obj.time || !region_obj.timeEnd) {
|
||||
// This is cut region
|
||||
if (isStartOfRegion(region_obj)) {
|
||||
region_obj.timeEnd = range.to.valueOf() - 1;
|
||||
} else {
|
||||
// Start time = null
|
||||
region_obj.timeEnd = region_obj.time;
|
||||
region_obj.time = range.from.valueOf() + 1;
|
||||
}
|
||||
region_obj.isRegion = true;
|
||||
}
|
||||
|
||||
return region_obj;
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
function isStartOfRegion(event): boolean {
|
||||
return event.id && event.id === event.regionId;
|
||||
}
|
||||
|
||||
export function dedupAnnotations(annotations) {
|
||||
let dedup = [];
|
||||
|
||||
// Split events by annotationId property existance
|
||||
let events = _.partition(annotations, 'id');
|
||||
|
||||
let eventsById = _.groupBy(events[0], 'id');
|
||||
dedup = _.map(eventsById, eventGroup => {
|
||||
if (eventGroup.length > 1 && !_.every(eventGroup, isPanelAlert)) {
|
||||
// Get first non-panel alert
|
||||
return _.find(eventGroup, event => {
|
||||
return event.eventType !== 'panel-alert';
|
||||
});
|
||||
} else {
|
||||
return _.head(eventGroup);
|
||||
}
|
||||
});
|
||||
|
||||
dedup = _.concat(dedup, events[1]);
|
||||
return dedup;
|
||||
}
|
||||
|
||||
function isPanelAlert(event) {
|
||||
return event.eventType === 'panel-alert';
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import {makeRegions, dedupAnnotations} from '../events_processing';
|
||||
|
||||
describe('Annotations', () => {
|
||||
|
||||
describe('Annotations regions', () => {
|
||||
let testAnnotations: any[];
|
||||
|
||||
beforeEach(() => {
|
||||
testAnnotations = [
|
||||
{id: 1, time: 1},
|
||||
{id: 2, time: 2},
|
||||
{id: 3, time: 3, regionId: 3},
|
||||
{id: 4, time: 5, regionId: 3},
|
||||
{id: 5, time: 4, regionId: 5},
|
||||
{id: 6, time: 8, regionId: 5}
|
||||
];
|
||||
});
|
||||
|
||||
it('should convert single region events to regions', () => {
|
||||
const range = {from: 0, to: 10};
|
||||
const expectedAnnotations = [
|
||||
{id: 3, regionId: 3, isRegion: true, time: 3, timeEnd: 5},
|
||||
{id: 5, regionId: 5, isRegion: true, time: 4, timeEnd: 8},
|
||||
{id: 1, time: 1},
|
||||
{id: 2, time: 2}
|
||||
];
|
||||
|
||||
let regions = makeRegions(testAnnotations, {range: range});
|
||||
expect(regions).toEqual(expectedAnnotations);
|
||||
});
|
||||
|
||||
it('should cut regions to current time range', () => {
|
||||
const range = {from: 0, to: 8};
|
||||
testAnnotations = [
|
||||
{id: 5, time: 4, regionId: 5}
|
||||
];
|
||||
const expectedAnnotations = [
|
||||
{id: 5, regionId: 5, isRegion: true, time: 4, timeEnd: 7}
|
||||
];
|
||||
|
||||
let regions = makeRegions(testAnnotations, {range: range});
|
||||
expect(regions).toEqual(expectedAnnotations);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Annotations deduplication', () => {
|
||||
it('should remove duplicated annotations', () => {
|
||||
const testAnnotations = [
|
||||
{id: 1, time: 1},
|
||||
{id: 2, time: 2},
|
||||
{id: 2, time: 2},
|
||||
{id: 5, time: 5},
|
||||
{id: 5, time: 5}
|
||||
];
|
||||
const expectedAnnotations = [
|
||||
{id: 1, time: 1},
|
||||
{id: 2, time: 2},
|
||||
{id: 5, time: 5}
|
||||
];
|
||||
|
||||
let deduplicated = dedupAnnotations(testAnnotations);
|
||||
expect(deduplicated).toEqual(expectedAnnotations);
|
||||
});
|
||||
|
||||
it('should leave non "panel-alert" event if present', () => {
|
||||
const testAnnotations = [
|
||||
{id: 1, time: 1},
|
||||
{id: 2, time: 2},
|
||||
{id: 2, time: 2, eventType: 'panel-alert'},
|
||||
{id: 5, time: 5},
|
||||
{id: 5, time: 5}
|
||||
];
|
||||
const expectedAnnotations = [
|
||||
{id: 1, time: 1},
|
||||
{id: 2, time: 2},
|
||||
{id: 5, time: 5}
|
||||
];
|
||||
|
||||
let deduplicated = dedupAnnotations(testAnnotations);
|
||||
expect(deduplicated).toEqual(expectedAnnotations);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,7 @@ import {coreModule, JsonExplorer} from 'app/core/core';
|
||||
const template = `
|
||||
<div class="query-troubleshooter" ng-if="ctrl.isOpen">
|
||||
<div class="query-troubleshooter__header">
|
||||
<a class="pointer" ng-click="ctrl.toggleMocking()">Mock Response</a>
|
||||
<a class="pointer" ng-click="ctrl.toggleExpand()" ng-hide="ctrl.allNodesExpanded">
|
||||
<i class="fa fa-plus-square-o"></i> Expand All
|
||||
</a>
|
||||
@@ -15,10 +16,15 @@ const template = `
|
||||
</a>
|
||||
<a class="pointer" clipboard-button="ctrl.getClipboardText()"><i class="fa fa-clipboard"></i> Copy to Clipboard</a>
|
||||
</div>
|
||||
<div class="query-troubleshooter__body">
|
||||
<div class="query-troubleshooter__body" ng-hide="ctrl.isMocking">
|
||||
<i class="fa fa-spinner fa-spin" ng-show="ctrl.isLoading"></i>
|
||||
<div class="query-troubleshooter-json"></div>
|
||||
</div>
|
||||
<div class="query-troubleshooter__body" ng-show="ctrl.isMocking">
|
||||
<div class="gf-form p-l-1 gf-form--v-stretch">
|
||||
<textarea class="gf-form-input" style="width: 95%" rows="10" ng-model="ctrl.mockedResponse" placeholder="JSON"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -32,6 +38,8 @@ export class QueryTroubleshooterCtrl {
|
||||
onRequestResponseEventListener: any;
|
||||
hasError: boolean;
|
||||
allNodesExpanded: boolean;
|
||||
isMocking: boolean;
|
||||
mockedResponse: string;
|
||||
jsonExplorer: JsonExplorer;
|
||||
|
||||
/** @ngInject **/
|
||||
@@ -51,6 +59,10 @@ export class QueryTroubleshooterCtrl {
|
||||
appEvents.off('ds-request-error', this.onRequestErrorEventListener);
|
||||
}
|
||||
|
||||
toggleMocking() {
|
||||
this.isMocking = !this.isMocking;
|
||||
}
|
||||
|
||||
onRequestError(err) {
|
||||
// ignore if closed
|
||||
if (!this.isOpen) {
|
||||
@@ -76,12 +88,29 @@ export class QueryTroubleshooterCtrl {
|
||||
return '';
|
||||
}
|
||||
|
||||
handleMocking(data) {
|
||||
var mockedData;
|
||||
try {
|
||||
mockedData = JSON.parse(this.mockedResponse);
|
||||
} catch (err) {
|
||||
appEvents.emit('alert-error', ['Failed to parse mocked response']);
|
||||
return;
|
||||
}
|
||||
|
||||
data.data = mockedData;
|
||||
}
|
||||
|
||||
onRequestResponse(data) {
|
||||
// ignore if closed
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isMocking) {
|
||||
this.handleMocking(data);
|
||||
return;
|
||||
}
|
||||
|
||||
this.isLoading = false;
|
||||
data = _.cloneDeep(data);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import * as flatten from 'app/core/utils/flatten';
|
||||
import * as ticks from 'app/core/utils/ticks';
|
||||
import {impressions} from 'app/features/dashboard/impression_store';
|
||||
import builtInPlugins from './built_in_plugins';
|
||||
import d3 from 'vendor/d3/d3';
|
||||
import * as d3 from 'd3';
|
||||
|
||||
// rxjs
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import {describe, it, expect} from 'test/lib/common';
|
||||
|
||||
import {containsVariable, assignModelProperties} from '../variable';
|
||||
|
||||
describe('containsVariable', function() {
|
||||
@@ -8,37 +6,37 @@ describe('containsVariable', function() {
|
||||
|
||||
it('should find it with $var syntax', function() {
|
||||
var contains = containsVariable('this.$test.filters', 'test');
|
||||
expect(contains).to.be(true);
|
||||
expect(contains).toBe(true);
|
||||
});
|
||||
|
||||
it('should not find it if only part matches with $var syntax', function() {
|
||||
var contains = containsVariable('this.$serverDomain.filters', 'server');
|
||||
expect(contains).to.be(false);
|
||||
expect(contains).toBe(false);
|
||||
});
|
||||
|
||||
it('should find it if it ends with variable and passing multiple test strings', function() {
|
||||
var contains = containsVariable('show field keys from $pgmetric', 'test string2', 'pgmetric');
|
||||
expect(contains).to.be(true);
|
||||
expect(contains).toBe(true);
|
||||
});
|
||||
|
||||
it('should find it with [[var]] syntax', function() {
|
||||
var contains = containsVariable('this.[[test]].filters', 'test');
|
||||
expect(contains).to.be(true);
|
||||
expect(contains).toBe(true);
|
||||
});
|
||||
|
||||
it('should find it when part of segment', function() {
|
||||
var contains = containsVariable('metrics.$env.$group-*', 'group');
|
||||
expect(contains).to.be(true);
|
||||
expect(contains).toBe(true);
|
||||
});
|
||||
|
||||
it('should find it its the only thing', function() {
|
||||
var contains = containsVariable('$env', 'env');
|
||||
expect(contains).to.be(true);
|
||||
expect(contains).toBe(true);
|
||||
});
|
||||
|
||||
it('should be able to pass in multiple test strings', function() {
|
||||
var contains = containsVariable('asd','asd2.$env', 'env');
|
||||
expect(contains).to.be(true);
|
||||
expect(contains).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -50,14 +48,14 @@ describe('assignModelProperties', function() {
|
||||
it('only set properties defined in defaults', function() {
|
||||
var target: any = {test: 'asd'};
|
||||
assignModelProperties(target, {propA: 1, propB: 2}, {propB: 0});
|
||||
expect(target.propB).to.be(2);
|
||||
expect(target.test).to.be('asd');
|
||||
expect(target.propB).toBe(2);
|
||||
expect(target.test).toBe('asd');
|
||||
});
|
||||
|
||||
it('use default value if not found on source', function() {
|
||||
var target: any = {test: 'asd'};
|
||||
assignModelProperties(target, {propA: 1, propB: 2}, {propC: 10});
|
||||
expect(target.propC).to.be(10);
|
||||
expect(target.propC).toBe(10);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,8 +1,6 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import _ from 'lodash';
|
||||
import TimeSeries from 'app/core/time_series2';
|
||||
import {colors} from 'app/core/core';
|
||||
import colors from 'app/core/utils/colors';
|
||||
|
||||
export class DataProcessor {
|
||||
|
||||
|
||||
@@ -381,7 +381,7 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
|
||||
var haveSortOrder = sortOrder !== null || sortOrder !== undefined;
|
||||
|
||||
if (panel.stack && haveSortBy && haveSortOrder) {
|
||||
var desc = desc = panel.legend.sortDesc === true ? 1 : -1;
|
||||
var desc = desc = panel.legend.sortDesc === true ? -1 : 1;
|
||||
series.sort((x, y) => {
|
||||
if (x.stats[sortBy] > y.stats[sortBy]) {
|
||||
return 1 * desc;
|
||||
|
||||
@@ -127,7 +127,7 @@ function ($, core) {
|
||||
value: value,
|
||||
hoverIndex: hoverIndex,
|
||||
color: series.color,
|
||||
label: series.label,
|
||||
label: series.aliasEscaped,
|
||||
time: pointTime,
|
||||
distance: hoverDistance,
|
||||
index: i
|
||||
@@ -264,7 +264,7 @@ function ($, core) {
|
||||
else if (item) {
|
||||
series = seriesList[item.seriesIndex];
|
||||
group = '<div class="graph-tooltip-list-item"><div class="graph-tooltip-series-name">';
|
||||
group += '<i class="fa fa-minus" style="color:' + item.series.color +';"></i> ' + series.label + ':</div>';
|
||||
group += '<i class="fa fa-minus" style="color:' + item.series.color +';"></i> ' + series.aliasEscaped + ':</div>';
|
||||
|
||||
if (panel.stack && panel.tooltip.value_type === 'individual') {
|
||||
value = item.datapoint[1] - item.datapoint[2];
|
||||
|
||||
@@ -169,7 +169,7 @@ function (angular, _, $) {
|
||||
html += '<i class="fa fa-minus pointer" style="color:' + series.color + '"></i>';
|
||||
html += '</div>';
|
||||
|
||||
html += '<a class="graph-legend-alias pointer" title="' + _.escape(series.label) + '">' + _.escape(series.label) + '</a>';
|
||||
html += '<a class="graph-legend-alias pointer" title="' + series.aliasEscaped + '">' + series.aliasEscaped + '</a>';
|
||||
|
||||
if (panel.legend.values) {
|
||||
var avg = series.formatValue(series.stats.avg);
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
///<reference path="../../../../headers/common.d.ts" />
|
||||
|
||||
import {describe, beforeEach, it, expect} from '../../../../../test/lib/common';
|
||||
|
||||
import {DataProcessor} from '../data_processor';
|
||||
|
||||
describe('Graph DataProcessor', function() {
|
||||
@@ -29,7 +25,7 @@ describe('Graph DataProcessor', function() {
|
||||
});
|
||||
|
||||
it('Should automatically set xaxis mode to field', () => {
|
||||
expect(panel.xaxis.mode).to.be('field');
|
||||
expect(panel.xaxis.mode).toBe('field');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -48,16 +44,16 @@ describe('Graph DataProcessor', function() {
|
||||
|
||||
it('Should return all field names', () => {
|
||||
var fields = processor.getDataFieldNames(dataList, false);
|
||||
expect(fields).to.contain('hostname');
|
||||
expect(fields).to.contain('valueField');
|
||||
expect(fields).to.contain('nested.prop1');
|
||||
expect(fields).to.contain('nested.value2');
|
||||
expect(fields).toContain('hostname');
|
||||
expect(fields).toContain('valueField');
|
||||
expect(fields).toContain('nested.prop1');
|
||||
expect(fields).toContain('nested.value2');
|
||||
});
|
||||
|
||||
it('Should return all number fields', () => {
|
||||
var fields = processor.getDataFieldNames(dataList, true);
|
||||
expect(fields).to.contain('valueField');
|
||||
expect(fields).to.contain('nested.value2');
|
||||
expect(fields).toContain('valueField');
|
||||
expect(fields).toContain('nested.value2');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -75,7 +75,7 @@ describe('grafanaGraph', function() {
|
||||
alias: 'series1'
|
||||
}));
|
||||
ctx.data.push(new TimeSeries({
|
||||
datapoints: [[1,10],[2,20]],
|
||||
datapoints: [[10,1],[20,2]],
|
||||
alias: 'series2'
|
||||
}));
|
||||
|
||||
@@ -112,52 +112,67 @@ describe('grafanaGraph', function() {
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('sort series as legend', (ctx) => {
|
||||
describe("with sort as legend undefined", () => {
|
||||
ctx.setup((ctrl) => {
|
||||
ctrl.panel.legend.sort = undefined;
|
||||
});
|
||||
|
||||
it("should not modify order of time series", () => {
|
||||
expect(ctx.plotData[0].alias).to.be('series1');
|
||||
expect(ctx.plotData[1].alias).to.be('series2');
|
||||
});
|
||||
graphScenario('sorting stacked series as legend. disabled', (ctx) => {
|
||||
ctx.setup((ctrl) => {
|
||||
ctrl.panel.legend.sort = undefined;
|
||||
ctrl.panel.stack = false;
|
||||
});
|
||||
|
||||
describe("with sort as legend set to min. descending order", () => {
|
||||
ctx.setup((ctrl) => {
|
||||
ctrl.panel.legend.sort = 'min';
|
||||
ctrl.panel.legend.sortDesc = true;
|
||||
});
|
||||
it("should not modify order of time series", () => {
|
||||
expect(ctx.plotData[0].alias).to.be('series1');
|
||||
expect(ctx.plotData[1].alias).to.be('series2');
|
||||
});
|
||||
});
|
||||
|
||||
it("highest value should be first", () => {
|
||||
expect(ctx.plotData[1].alias).to.be('series2');
|
||||
expect(ctx.plotData[0].alias).to.be('series1');
|
||||
});
|
||||
graphScenario("sorting stacked series as legend. min descending order", (ctx) => {
|
||||
ctx.setup(ctrl => {
|
||||
ctrl.panel.legend.sort = 'min';
|
||||
ctrl.panel.legend.sortDesc = true;
|
||||
ctrl.panel.stack = true;
|
||||
});
|
||||
|
||||
describe("with sort as legend set to min. ascending order", () => {
|
||||
ctx.setup((ctrl) => {
|
||||
ctrl.panel.legend.sort = 'min';
|
||||
ctrl.panel.legend.sortDesc = true;
|
||||
});
|
||||
it("highest value should be first", () => {
|
||||
expect(ctx.plotData[0].alias).to.be('series2');
|
||||
expect(ctx.plotData[1].alias).to.be('series1');
|
||||
});
|
||||
});
|
||||
|
||||
it("lowest value should be first", () => {
|
||||
expect(ctx.plotData[0].alias).to.be('series1');
|
||||
expect(ctx.plotData[1].alias).to.be('series2');
|
||||
});
|
||||
graphScenario("sorting stacked series as legend. min ascending order", (ctx) => {
|
||||
ctx.setup((ctrl, data) => {
|
||||
ctrl.panel.legend.sort = 'min';
|
||||
ctrl.panel.legend.sortDesc = false;
|
||||
ctrl.panel.stack = true;
|
||||
});
|
||||
|
||||
describe("with sort as legend set to current. ascending order", () => {
|
||||
ctx.setup((ctrl) => {
|
||||
ctrl.panel.legend.sort = 'current';
|
||||
ctrl.panel.legend.sortDesc = false;
|
||||
});
|
||||
it("lowest value should be first", () => {
|
||||
expect(ctx.plotData[0].alias).to.be('series1');
|
||||
expect(ctx.plotData[1].alias).to.be('series2');
|
||||
});
|
||||
});
|
||||
|
||||
it("highest last value should be first", () => {
|
||||
expect(ctx.plotData[1].alias).to.be('series2');
|
||||
expect(ctx.plotData[0].alias).to.be('series1');
|
||||
});
|
||||
graphScenario("sorting stacked series as legend. stacking disabled", (ctx) => {
|
||||
ctx.setup((ctrl) => {
|
||||
ctrl.panel.legend.sort = 'min';
|
||||
ctrl.panel.legend.sortDesc = true;
|
||||
ctrl.panel.stack = false;
|
||||
});
|
||||
|
||||
it("highest value should be first", () => {
|
||||
expect(ctx.plotData[0].alias).to.be('series1');
|
||||
expect(ctx.plotData[1].alias).to.be('series2');
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario("sorting stacked series as legend. current descending order", (ctx) => {
|
||||
ctx.setup((ctrl) => {
|
||||
ctrl.panel.legend.sort = 'current';
|
||||
ctrl.panel.legend.sortDesc = true;
|
||||
ctrl.panel.stack = true;
|
||||
});
|
||||
|
||||
it("highest last value should be first", () => {
|
||||
expect(ctx.plotData[0].alias).to.be('series2');
|
||||
expect(ctx.plotData[1].alias).to.be('series1');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -300,7 +315,7 @@ describe('grafanaGraph', function() {
|
||||
});
|
||||
|
||||
it('should set barWidth', function() {
|
||||
expect(ctx.plotOptions.series.bars.barWidth).to.be(10/1.5);
|
||||
expect(ctx.plotOptions.series.bars.barWidth).to.be(1/1.5);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -383,146 +398,4 @@ describe('grafanaGraph', function() {
|
||||
});
|
||||
|
||||
}, 10);
|
||||
|
||||
// graphScenario('when using flexible Y-Min and Y-Max settings', function(ctx) {
|
||||
// describe('and Y-Min is <100 and Y-Max is >200 and values within range', function() {
|
||||
// ctx.setup(function(ctrl, data) {
|
||||
// ctrl.panel.yaxes[0].min = '<100';
|
||||
// ctrl.panel.yaxes[0].max = '>200';
|
||||
// data[0] = new TimeSeries({
|
||||
// datapoints: [[120,10],[160,20]],
|
||||
// alias: 'series1',
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it('should set min to 100 and max to 200', function() {
|
||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(100);
|
||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(200);
|
||||
// });
|
||||
// });
|
||||
// describe('and Y-Min is <100 and Y-Max is >200 and values outside range', function() {
|
||||
// ctx.setup(function(ctrl, data) {
|
||||
// ctrl.panel.yaxes[0].min = '<100';
|
||||
// ctrl.panel.yaxes[0].max = '>200';
|
||||
// data[0] = new TimeSeries({
|
||||
// datapoints: [[99,10],[201,20]],
|
||||
// alias: 'series1',
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it('should set min to auto and max to auto', function() {
|
||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(null);
|
||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(null);
|
||||
// });
|
||||
// });
|
||||
// describe('and Y-Min is =10.5 and Y-Max is =10.5', function() {
|
||||
// ctx.setup(function(ctrl, data) {
|
||||
// ctrl.panel.yaxes[0].min = '=10.5';
|
||||
// ctrl.panel.yaxes[0].max = '=10.5';
|
||||
// data[0] = new TimeSeries({
|
||||
// datapoints: [[100,10],[120,20], [110,30]],
|
||||
// alias: 'series1',
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it('should set min to last value + 10.5 and max to last value + 10.5', function() {
|
||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(99.5);
|
||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(120.5);
|
||||
// });
|
||||
// });
|
||||
// describe('and Y-Min is ~10.5 and Y-Max is ~10.5', function() {
|
||||
// ctx.setup(function(ctrl, data) {
|
||||
// ctrl.panel.yaxes[0].min = '~10.5';
|
||||
// ctrl.panel.yaxes[0].max = '~10.5';
|
||||
// data[0] = new TimeSeries({
|
||||
// datapoints: [[102,10],[104,20], [110,30]], //Also checks precision
|
||||
// alias: 'series1',
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it('should set min to average value + 10.5 and max to average value + 10.5', function() {
|
||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(94.8);
|
||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(115.8);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// graphScenario('when using regular Y-Min and Y-Max settings', function(ctx) {
|
||||
// describe('and Y-Min is 100 and Y-Max is 200', function() {
|
||||
// ctx.setup(function(ctrl, data) {
|
||||
// ctrl.panel.yaxes[0].min = '100';
|
||||
// ctrl.panel.yaxes[0].max = '200';
|
||||
// data[0] = new TimeSeries({
|
||||
// datapoints: [[120,10],[160,20]],
|
||||
// alias: 'series1',
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it('should set min to 100 and max to 200', function() {
|
||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(100);
|
||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(200);
|
||||
// });
|
||||
// });
|
||||
// describe('and Y-Min is 0 and Y-Max is 0', function() {
|
||||
// ctx.setup(function(ctrl, data) {
|
||||
// ctrl.panel.yaxes[0].min = '0';
|
||||
// ctrl.panel.yaxes[0].max = '0';
|
||||
// data[0] = new TimeSeries({
|
||||
// datapoints: [[120,10],[160,20]],
|
||||
// alias: 'series1',
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it('should set min to 0 and max to 0', function() {
|
||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(0);
|
||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(0);
|
||||
// });
|
||||
// });
|
||||
// describe('and negative values used', function() {
|
||||
// ctx.setup(function(ctrl, data) {
|
||||
// ctrl.panel.yaxes[0].min = '-10';
|
||||
// ctrl.panel.yaxes[0].max = '-13.14';
|
||||
// data[0] = new TimeSeries({
|
||||
// datapoints: [[120,10],[160,20]],
|
||||
// alias: 'series1',
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it('should set min and max to negative', function() {
|
||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(-10);
|
||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(-13.14);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// graphScenario('when using Y-Min and Y-Max settings stored as number', function(ctx) {
|
||||
// describe('and Y-Min is 0 and Y-Max is 100', function() {
|
||||
// ctx.setup(function(ctrl, data) {
|
||||
// ctrl.panel.yaxes[0].min = 0;
|
||||
// ctrl.panel.yaxes[0].max = 100;
|
||||
// data[0] = new TimeSeries({
|
||||
// datapoints: [[120,10],[160,20]],
|
||||
// alias: 'series1',
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it('should set min to 0 and max to 100', function() {
|
||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(0);
|
||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(100);
|
||||
// });
|
||||
// });
|
||||
// describe('and Y-Min is -100 and Y-Max is -10.5', function() {
|
||||
// ctx.setup(function(ctrl, data) {
|
||||
// ctrl.panel.yaxes[0].min = -100;
|
||||
// ctrl.panel.yaxes[0].max = -10.5;
|
||||
// data[0] = new TimeSeries({
|
||||
// datapoints: [[120,10],[160,20]],
|
||||
// alias: 'series1',
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it('should set min to -100 and max to -10.5', function() {
|
||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(-100);
|
||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(-10.5);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
});
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
///<reference path="../../../../headers/common.d.ts" />
|
||||
|
||||
import { describe, beforeEach, it, expect } from '../../../../../test/lib/common';
|
||||
|
||||
import { convertValuesToHistogram, getSeriesValues } from '../histogram';
|
||||
|
||||
describe('Graph Histogam Converter', function () {
|
||||
@@ -21,7 +17,7 @@ describe('Graph Histogam Converter', function () {
|
||||
];
|
||||
|
||||
let histogram = convertValuesToHistogram(values, bucketSize);
|
||||
expect(histogram).to.eql(expected);
|
||||
expect(histogram).toMatchObject(expected);
|
||||
});
|
||||
|
||||
it('Should not add empty buckets', () => {
|
||||
@@ -31,7 +27,7 @@ describe('Graph Histogam Converter', function () {
|
||||
];
|
||||
|
||||
let histogram = convertValuesToHistogram(values, bucketSize);
|
||||
expect(histogram).to.eql(expected);
|
||||
expect(histogram).toMatchObject(expected);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,7 +46,7 @@ describe('Graph Histogam Converter', function () {
|
||||
let expected = [1, 2, 10, 11, 17, 20, 29];
|
||||
|
||||
let values = getSeriesValues(data);
|
||||
expect(values).to.eql(expected);
|
||||
expect(values).toMatchObject(expected);
|
||||
});
|
||||
|
||||
it('Should skip null values', () => {
|
||||
@@ -59,7 +55,7 @@ describe('Graph Histogam Converter', function () {
|
||||
let expected = [1, 2, 10, 11, 17, 20, 29];
|
||||
|
||||
let values = getSeriesValues(data);
|
||||
expect(values).to.eql(expected);
|
||||
expect(values).toMatchObject(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,10 @@
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import d3 from 'vendor/d3/d3';
|
||||
import * as d3 from 'd3';
|
||||
import {contextSrv} from 'app/core/core';
|
||||
import {tickStep} from 'app/core/utils/ticks';
|
||||
import {getColorScale, getOpacityScale} from './color_scale';
|
||||
|
||||
let module = angular.module('grafana.directives');
|
||||
|
||||
@@ -30,7 +31,7 @@ module.directive('colorLegend', function() {
|
||||
|
||||
if (panel.color.mode === 'spectrum') {
|
||||
let colorScheme = _.find(ctrl.colorSchemes, {value: panel.color.colorScheme});
|
||||
let colorScale = getColorScale(colorScheme, legendWidth);
|
||||
let colorScale = getColorScale(colorScheme, contextSrv.user.lightTheme, legendWidth);
|
||||
drawSimpleColorLegend(elem, colorScale);
|
||||
} else if (panel.color.mode === 'opacity') {
|
||||
let colorOptions = panel.color;
|
||||
@@ -93,7 +94,7 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal
|
||||
let widthFactor = legendWidth / (rangeTo - rangeFrom);
|
||||
let valuesRange = d3.range(rangeFrom, rangeTo, rangeStep);
|
||||
|
||||
let colorScale = getColorScale(colorScheme, maxValue, minValue);
|
||||
let colorScale = getColorScale(colorScheme, contextSrv.user.lightTheme, maxValue, minValue);
|
||||
legend.selectAll(".heatmap-color-legend-rect")
|
||||
.data(valuesRange)
|
||||
.enter().append("rect")
|
||||
@@ -115,7 +116,10 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue
|
||||
let legendWidth = Math.floor(legendElem.outerWidth()) - 30;
|
||||
let legendHeight = legendElem.attr("height");
|
||||
|
||||
let rangeStep = 10;
|
||||
let rangeStep = 1;
|
||||
if (rangeTo - rangeFrom > legendWidth) {
|
||||
rangeStep = Math.floor((rangeTo - rangeFrom) / legendWidth);
|
||||
}
|
||||
let widthFactor = legendWidth / (rangeTo - rangeFrom);
|
||||
let valuesRange = d3.range(rangeFrom, rangeTo, rangeStep);
|
||||
|
||||
@@ -228,31 +232,6 @@ function clearLegend(elem) {
|
||||
legendElem.empty();
|
||||
}
|
||||
|
||||
function getColorScale(colorScheme, maxValue, minValue = 0) {
|
||||
let colorInterpolator = d3[colorScheme.value];
|
||||
let colorScaleInverted = colorScheme.invert === 'always' ||
|
||||
(colorScheme.invert === 'dark' && !contextSrv.user.lightTheme);
|
||||
|
||||
let start = colorScaleInverted ? maxValue : minValue;
|
||||
let end = colorScaleInverted ? minValue : maxValue;
|
||||
|
||||
return d3.scaleSequential(colorInterpolator).domain([start, end]);
|
||||
}
|
||||
|
||||
function getOpacityScale(options, maxValue, minValue = 0) {
|
||||
let legendOpacityScale;
|
||||
if (options.colorScale === 'linear') {
|
||||
legendOpacityScale = d3.scaleLinear()
|
||||
.domain([minValue, maxValue])
|
||||
.range([0, 1]);
|
||||
} else if (options.colorScale === 'sqrt') {
|
||||
legendOpacityScale = d3.scalePow().exponent(options.exponent)
|
||||
.domain([minValue, maxValue])
|
||||
.range([0, 1]);
|
||||
}
|
||||
return legendOpacityScale;
|
||||
}
|
||||
|
||||
function getSvgElemX(elem) {
|
||||
let svgElem = elem.get(0);
|
||||
if (svgElem && svgElem.x && svgElem.x.baseVal) {
|
||||
|
||||
27
public/app/plugins/panel/heatmap/color_scale.ts
Normal file
27
public/app/plugins/panel/heatmap/color_scale.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import * as d3 from 'd3';
|
||||
import * as d3ScaleChromatic from 'd3-scale-chromatic';
|
||||
|
||||
export function getColorScale(colorScheme: any, lightTheme: boolean, maxValue: number, minValue = 0): (d: any) => any {
|
||||
let colorInterpolator = d3ScaleChromatic[colorScheme.value];
|
||||
let colorScaleInverted = colorScheme.invert === 'always' ||
|
||||
(colorScheme.invert === 'dark' && !lightTheme);
|
||||
|
||||
let start = colorScaleInverted ? maxValue : minValue;
|
||||
let end = colorScaleInverted ? minValue : maxValue;
|
||||
|
||||
return d3.scaleSequential(colorInterpolator).domain([start, end]);
|
||||
}
|
||||
|
||||
export function getOpacityScale(options, maxValue, minValue = 0) {
|
||||
let legendOpacityScale;
|
||||
if (options.colorScale === 'linear') {
|
||||
legendOpacityScale = d3.scaleLinear()
|
||||
.domain([minValue, maxValue])
|
||||
.range([0, 1]);
|
||||
} else if (options.colorScale === 'sqrt') {
|
||||
legendOpacityScale = d3.scalePow().exponent(options.exponent)
|
||||
.domain([minValue, maxValue])
|
||||
.range([0, 1]);
|
||||
}
|
||||
return legendOpacityScale;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import d3 from 'vendor/d3/d3';
|
||||
import * as d3 from 'd3';
|
||||
import $ from 'jquery';
|
||||
import _ from 'lodash';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import moment from 'moment';
|
||||
import * as d3 from 'd3';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import {appEvents, contextSrv} from 'app/core/core';
|
||||
import {tickStep, getScaledDecimals, getFlotTickSize} from 'app/core/utils/ticks';
|
||||
import d3 from 'vendor/d3/d3';
|
||||
import {HeatmapTooltip} from './heatmap_tooltip';
|
||||
import {mergeZeroBuckets} from './heatmap_data_converter';
|
||||
import {getColorScale, getOpacityScale} from './color_scale';
|
||||
|
||||
let MIN_CARD_SIZE = 1,
|
||||
CARD_PADDING = 1,
|
||||
@@ -386,8 +387,9 @@ export default function link(scope, elem, attrs, ctrl) {
|
||||
let maxValue = panel.color.max || maxValueAuto;
|
||||
let minValue = panel.color.min || 0;
|
||||
|
||||
colorScale = getColorScale(maxValue, minValue);
|
||||
setOpacityScale(maxValue);
|
||||
let colorScheme = _.find(ctrl.colorSchemes, {value: panel.color.colorScheme});
|
||||
colorScale = getColorScale(colorScheme, contextSrv.user.lightTheme, maxValue, minValue);
|
||||
opacityScale = getOpacityScale(panel.color, maxValue);
|
||||
setCardSize();
|
||||
|
||||
let cards = heatmap.selectAll(".heatmap-card").data(cardsData);
|
||||
@@ -422,8 +424,8 @@ export default function link(scope, elem, attrs, ctrl) {
|
||||
let strokeColor = d3.color(color).brighter(4);
|
||||
let current_card = d3.select(event.target);
|
||||
tooltip.originalFillColor = color;
|
||||
current_card.style("fill", highlightColor)
|
||||
.style("stroke", strokeColor)
|
||||
current_card.style("fill", highlightColor.toString())
|
||||
.style("stroke", strokeColor.toString())
|
||||
.style("stroke-width", 1);
|
||||
}
|
||||
|
||||
@@ -433,30 +435,6 @@ export default function link(scope, elem, attrs, ctrl) {
|
||||
.style("stroke-width", 0);
|
||||
}
|
||||
|
||||
function getColorScale(maxValue, minValue = 0) {
|
||||
let colorScheme = _.find(ctrl.colorSchemes, {value: panel.color.colorScheme});
|
||||
let colorInterpolator = d3[colorScheme.value];
|
||||
let colorScaleInverted = colorScheme.invert === 'always' ||
|
||||
(colorScheme.invert === 'dark' && !contextSrv.user.lightTheme);
|
||||
|
||||
let start = colorScaleInverted ? maxValue : minValue;
|
||||
let end = colorScaleInverted ? minValue : maxValue;
|
||||
|
||||
return d3.scaleSequential(colorInterpolator).domain([start, end]);
|
||||
}
|
||||
|
||||
function setOpacityScale(maxValue) {
|
||||
if (panel.color.colorScale === 'linear') {
|
||||
opacityScale = d3.scaleLinear()
|
||||
.domain([0, maxValue])
|
||||
.range([0, 1]);
|
||||
} else if (panel.color.colorScale === 'sqrt') {
|
||||
opacityScale = d3.scalePow().exponent(panel.color.exponent)
|
||||
.domain([0, maxValue])
|
||||
.range([0, 1]);
|
||||
}
|
||||
}
|
||||
|
||||
function setCardSize() {
|
||||
let xGridSize = Math.floor(xScale(data.xBucketSize) - xScale(0));
|
||||
let yGridSize = Math.floor(yScale(yScale.invert(0) - data.yBucketSize));
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
System.config({
|
||||
defaultJSExtenions: true,
|
||||
baseURL: 'public',
|
||||
paths: {
|
||||
'virtual-scroll': 'vendor/npm/virtual-scroll/src/index.js',
|
||||
'mousetrap': 'vendor/npm/mousetrap/mousetrap.js',
|
||||
'remarkable': 'vendor/npm/remarkable/dist/remarkable.js',
|
||||
'tether': 'vendor/npm/tether/dist/js/tether.js',
|
||||
'eventemitter3': 'vendor/npm/eventemitter3/index.js',
|
||||
'tether-drop': 'vendor/npm/tether-drop/dist/js/drop.js',
|
||||
'moment': 'vendor/moment.js',
|
||||
"jquery": "vendor/jquery/dist/jquery.js",
|
||||
'lodash-src': 'vendor/lodash/dist/lodash.js',
|
||||
"lodash": 'app/core/lodash_extended.js',
|
||||
"angular": "vendor/angular/angular.js",
|
||||
"bootstrap": "vendor/bootstrap/bootstrap.js",
|
||||
'angular-route': 'vendor/angular-route/angular-route.js',
|
||||
'angular-sanitize': 'vendor/angular-sanitize/angular-sanitize.js',
|
||||
"angular-ui": "vendor/angular-ui/ui-bootstrap-tpls.js",
|
||||
"angular-strap": "vendor/angular-other/angular-strap.js",
|
||||
"angular-dragdrop": "vendor/angular-native-dragdrop/draganddrop.js",
|
||||
"angular-bindonce": "vendor/angular-bindonce/bindonce.js",
|
||||
"spectrum": "vendor/spectrum.js",
|
||||
"bootstrap-tagsinput": "vendor/tagsinput/bootstrap-tagsinput.js",
|
||||
"jquery.flot": "vendor/flot/jquery.flot",
|
||||
"jquery.flot.pie": "vendor/flot/jquery.flot.pie",
|
||||
"jquery.flot.selection": "vendor/flot/jquery.flot.selection",
|
||||
"jquery.flot.stack": "vendor/flot/jquery.flot.stack",
|
||||
"jquery.flot.stackpercent": "vendor/flot/jquery.flot.stackpercent",
|
||||
"jquery.flot.time": "vendor/flot/jquery.flot.time",
|
||||
"jquery.flot.crosshair": "vendor/flot/jquery.flot.crosshair",
|
||||
"jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow",
|
||||
"jquery.flot.gauge": "vendor/flot/jquery.flot.gauge",
|
||||
"d3": "vendor/d3/d3.js",
|
||||
"jquery.flot.dashes": "vendor/flot/jquery.flot.dashes",
|
||||
"twemoji": "vendor/npm/twemoji/2/twemoji.amd.js",
|
||||
"ace": "vendor/npm/ace-builds/src-noconflict/ace",
|
||||
},
|
||||
|
||||
packages: {
|
||||
app: {
|
||||
defaultExtension: 'js',
|
||||
},
|
||||
vendor: {
|
||||
defaultExtension: 'js',
|
||||
},
|
||||
plugins: {
|
||||
defaultExtension: 'js',
|
||||
},
|
||||
test: {
|
||||
defaultExtension: 'js',
|
||||
},
|
||||
},
|
||||
|
||||
map: {
|
||||
text: 'vendor/plugin-text/text.js',
|
||||
css: 'app/core/utils/css_loader.js'
|
||||
},
|
||||
|
||||
meta: {
|
||||
'vendor/npm/virtual-scroll/src/indx.js': {
|
||||
format: 'cjs',
|
||||
exports: 'VirtualScroll',
|
||||
},
|
||||
'vendor/angular/angular.js': {
|
||||
format: 'global',
|
||||
deps: ['jquery'],
|
||||
exports: 'angular',
|
||||
},
|
||||
'vendor/npm/eventemitter3/index.js': {
|
||||
format: 'cjs',
|
||||
exports: 'EventEmitter'
|
||||
},
|
||||
'vendor/npm/mousetrap/mousetrap.js': {
|
||||
format: 'global',
|
||||
exports: 'Mousetrap'
|
||||
},
|
||||
'vendor/npm/ace-builds/src-noconflict/ace.js': {
|
||||
format: 'global',
|
||||
exports: 'ace'
|
||||
}
|
||||
}
|
||||
});
|
||||
27
public/vendor/d3/LICENSE
vendored
27
public/vendor/d3/LICENSE
vendored
@@ -1,27 +0,0 @@
|
||||
Copyright 2010-2016 Mike Bostock
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the author nor the names of contributors may be used to
|
||||
endorse or promote products derived from this software without specific prior
|
||||
written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
57
public/vendor/d3/README.md
vendored
57
public/vendor/d3/README.md
vendored
@@ -1,57 +0,0 @@
|
||||
# D3: Data-Driven Documents
|
||||
|
||||
<a href="https://d3js.org"><img src="https://d3js.org/logo.svg" align="left" hspace="10" vspace="6"></a>
|
||||
|
||||
**D3** (or **D3.js**) is a JavaScript library for visualizing data using web standards. D3 helps you bring data to life using SVG, Canvas and HTML. D3 combines powerful visualization and interaction techniques with a data-driven approach to DOM manipulation, giving you the full capabilities of modern browsers and the freedom to design the right visual interface for your data.
|
||||
|
||||
## Resources
|
||||
|
||||
* [API Reference](https://github.com/d3/d3/blob/master/API.md)
|
||||
* [Release Notes](https://github.com/d3/d3/releases)
|
||||
* [Gallery](https://github.com/d3/d3/wiki/Gallery)
|
||||
* [Examples](http://bl.ocks.org/mbostock)
|
||||
* [Wiki](https://github.com/d3/d3/wiki)
|
||||
|
||||
## Installing
|
||||
|
||||
If you use npm, `npm install d3`. Otherwise, download the [latest release](https://github.com/d3/d3/releases/latest). The released bundle supports anonymous AMD, CommonJS, and vanilla environments. You can load directly from [d3js.org](https://d3js.org), [CDNJS](https://cdnjs.com/libraries/d3), or [unpkg](https://unpkg.com/d3/). For example:
|
||||
|
||||
```html
|
||||
<script src="https://d3js.org/d3.v4.js"></script>
|
||||
```
|
||||
|
||||
For the minified version:
|
||||
|
||||
```html
|
||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
||||
```
|
||||
|
||||
You can also use the standalone D3 microlibraries. For example, [d3-selection](https://github.com/d3/d3-selection):
|
||||
|
||||
```html
|
||||
<script src="https://d3js.org/d3-selection.v1.js"></script>
|
||||
```
|
||||
|
||||
D3 is written using [ES2015 modules](http://www.2ality.com/2014/09/es6-modules-final.html). Create a [custom bundle using Rollup](http://bl.ocks.org/mbostock/bb09af4c39c79cffcde4), Webpack, or your preferred bundler. To import D3 into an ES2015 application, either import specific symbols from specific D3 modules:
|
||||
|
||||
```js
|
||||
import {scaleLinear} from "d3-scale";
|
||||
```
|
||||
|
||||
Or import everything into a namespace (here, `d3`):
|
||||
|
||||
```js
|
||||
import * as d3 from "d3";
|
||||
```
|
||||
|
||||
In Node:
|
||||
|
||||
```js
|
||||
var d3 = require("d3");
|
||||
```
|
||||
|
||||
You can also require individual modules and combine them into a `d3` object using [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign):
|
||||
|
||||
```js
|
||||
var d3 = Object.assign({}, require("d3-format"), require("d3-geo"), require("d3-geo-projection"));
|
||||
```
|
||||
2
public/vendor/d3/d3-scale-chromatic.min.js
vendored
2
public/vendor/d3/d3-scale-chromatic.min.js
vendored
File diff suppressed because one or more lines are too long
3
public/vendor/d3/d3.js
vendored
3
public/vendor/d3/d3.js
vendored
@@ -1,3 +0,0 @@
|
||||
// Import main D3.js module and combine it with another
|
||||
var d3 = Object.assign({}, require('./d3.v4.min.js'), require('./d3-scale-chromatic.min.js'));
|
||||
module.exports = d3;
|
||||
8
public/vendor/d3/d3.v4.min.js
vendored
8
public/vendor/d3/d3.v4.min.js
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user