mirror of https://github.com/grafana/grafana
Merge pull request #10994 from ilgizar/1271_share_zero
commit
1ee0d1c296
@ -0,0 +1,154 @@ |
||||
import _ from 'lodash'; |
||||
|
||||
/** |
||||
* To align two Y axes by Y level |
||||
* @param yAxes data [{min: min_y1, min: max_y1}, {min: min_y2, max: max_y2}] |
||||
* @param level Y level |
||||
*/ |
||||
export function alignYLevel(yAxes, level) { |
||||
if (isNaN(level) || !checkCorrectAxis(yAxes)) { |
||||
return; |
||||
} |
||||
|
||||
var [yLeft, yRight] = yAxes; |
||||
moveLevelToZero(yLeft, yRight, level); |
||||
|
||||
expandStuckValues(yLeft, yRight); |
||||
|
||||
// one of graphs on zero
|
||||
var zero = yLeft.min === 0 || yRight.min === 0 || yLeft.max === 0 || yRight.max === 0; |
||||
|
||||
var oneSide = checkOneSide(yLeft, yRight); |
||||
|
||||
if (zero && oneSide) { |
||||
yLeft.min = yLeft.max > 0 ? 0 : yLeft.min; |
||||
yLeft.max = yLeft.max > 0 ? yLeft.max : 0; |
||||
yRight.min = yRight.max > 0 ? 0 : yRight.min; |
||||
yRight.max = yRight.max > 0 ? yRight.max : 0; |
||||
} else { |
||||
if (checkOppositeSides(yLeft, yRight)) { |
||||
if (yLeft.min >= 0) { |
||||
yLeft.min = -yLeft.max; |
||||
yRight.max = -yRight.min; |
||||
} else { |
||||
yLeft.max = -yLeft.min; |
||||
yRight.min = -yRight.max; |
||||
} |
||||
} else { |
||||
var rate = getRate(yLeft, yRight); |
||||
|
||||
if (oneSide) { |
||||
// all graphs above the Y level
|
||||
if (yLeft.min > 0) { |
||||
yLeft.min = yLeft.max / rate; |
||||
yRight.min = yRight.max / rate; |
||||
} else { |
||||
yLeft.max = yLeft.min / rate; |
||||
yRight.max = yRight.min / rate; |
||||
} |
||||
} else { |
||||
if (checkTwoCross(yLeft, yRight)) { |
||||
yLeft.min = yRight.min ? yRight.min * rate : yLeft.min; |
||||
yRight.min = yLeft.min ? yLeft.min / rate : yRight.min; |
||||
yLeft.max = yRight.max ? yRight.max * rate : yLeft.max; |
||||
yRight.max = yLeft.max ? yLeft.max / rate : yRight.max; |
||||
} else { |
||||
yLeft.min = yLeft.min > 0 ? yRight.min * rate : yLeft.min; |
||||
yRight.min = yRight.min > 0 ? yLeft.min / rate : yRight.min; |
||||
yLeft.max = yLeft.max < 0 ? yRight.max * rate : yLeft.max; |
||||
yRight.max = yRight.max < 0 ? yLeft.max / rate : yRight.max; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
restoreLevelFromZero(yLeft, yRight, level); |
||||
} |
||||
|
||||
function expandStuckValues(yLeft, yRight) { |
||||
// wide Y min and max using increased wideFactor
|
||||
var wideFactor = 0.25; |
||||
if (yLeft.max === yLeft.min) { |
||||
yLeft.min -= wideFactor; |
||||
yLeft.max += wideFactor; |
||||
} |
||||
if (yRight.max === yRight.min) { |
||||
yRight.min -= wideFactor; |
||||
yRight.max += wideFactor; |
||||
} |
||||
} |
||||
|
||||
function moveLevelToZero(yLeft, yRight, level) { |
||||
if (level !== 0) { |
||||
yLeft.min -= level; |
||||
yLeft.max -= level; |
||||
yRight.min -= level; |
||||
yRight.max -= level; |
||||
} |
||||
} |
||||
|
||||
function restoreLevelFromZero(yLeft, yRight, level) { |
||||
if (level !== 0) { |
||||
yLeft.min += level; |
||||
yLeft.max += level; |
||||
yRight.min += level; |
||||
yRight.max += level; |
||||
} |
||||
} |
||||
|
||||
function checkCorrectAxis(axis) { |
||||
return axis.length === 2 && checkCorrectAxes(axis[0]) && checkCorrectAxes(axis[1]); |
||||
} |
||||
|
||||
function checkCorrectAxes(axes) { |
||||
return 'min' in axes && 'max' in axes; |
||||
} |
||||
|
||||
function checkOneSide(yLeft, yRight) { |
||||
// on the one hand with respect to zero
|
||||
return (yLeft.min >= 0 && yRight.min >= 0) || (yLeft.max <= 0 && yRight.max <= 0); |
||||
} |
||||
|
||||
function checkTwoCross(yLeft, yRight) { |
||||
// both across zero
|
||||
return yLeft.min <= 0 && yLeft.max >= 0 && yRight.min <= 0 && yRight.max >= 0; |
||||
} |
||||
|
||||
function checkOppositeSides(yLeft, yRight) { |
||||
// on the opposite sides with respect to zero
|
||||
return (yLeft.min >= 0 && yRight.max <= 0) || (yLeft.max <= 0 && yRight.min >= 0); |
||||
} |
||||
|
||||
function getRate(yLeft, yRight) { |
||||
var rateLeft, rateRight, rate; |
||||
if (checkTwoCross(yLeft, yRight)) { |
||||
rateLeft = yRight.min ? yLeft.min / yRight.min : 0; |
||||
rateRight = yRight.max ? yLeft.max / yRight.max : 0; |
||||
} else { |
||||
if (checkOneSide(yLeft, yRight)) { |
||||
var absLeftMin = Math.abs(yLeft.min); |
||||
var absLeftMax = Math.abs(yLeft.max); |
||||
var absRightMin = Math.abs(yRight.min); |
||||
var absRightMax = Math.abs(yRight.max); |
||||
var upLeft = _.max([absLeftMin, absLeftMax]); |
||||
var downLeft = _.min([absLeftMin, absLeftMax]); |
||||
var upRight = _.max([absRightMin, absRightMax]); |
||||
var downRight = _.min([absRightMin, absRightMax]); |
||||
|
||||
rateLeft = downLeft ? upLeft / downLeft : upLeft; |
||||
rateRight = downRight ? upRight / downRight : upRight; |
||||
} else { |
||||
if (yLeft.min > 0 || yRight.min > 0) { |
||||
rateLeft = yLeft.max / yRight.max; |
||||
rateRight = 0; |
||||
} else { |
||||
rateLeft = 0; |
||||
rateRight = yLeft.min / yRight.min; |
||||
} |
||||
} |
||||
} |
||||
|
||||
rate = rateLeft > rateRight ? rateLeft : rateRight; |
||||
|
||||
return rate; |
||||
} |
@ -0,0 +1,210 @@ |
||||
import { alignYLevel } from '../align_yaxes'; |
||||
|
||||
describe('Graph Y axes aligner', function() { |
||||
let yaxes, expected; |
||||
let alignY = 0; |
||||
|
||||
describe('on the one hand with respect to zero', () => { |
||||
it('Should shrink Y axis', () => { |
||||
yaxes = [{ min: 5, max: 10 }, { min: 2, max: 3 }]; |
||||
expected = [{ min: 5, max: 10 }, { min: 1.5, max: 3 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axis', () => { |
||||
yaxes = [{ min: 2, max: 3 }, { min: 5, max: 10 }]; |
||||
expected = [{ min: 1.5, max: 3 }, { min: 5, max: 10 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axis', () => { |
||||
yaxes = [{ min: -10, max: -5 }, { min: -3, max: -2 }]; |
||||
expected = [{ min: -10, max: -5 }, { min: -3, max: -1.5 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axis', () => { |
||||
yaxes = [{ min: -3, max: -2 }, { min: -10, max: -5 }]; |
||||
expected = [{ min: -3, max: -1.5 }, { min: -10, max: -5 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
}); |
||||
|
||||
describe('on the opposite sides with respect to zero', () => { |
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: -3, max: -1 }, { min: 5, max: 10 }]; |
||||
expected = [{ min: -3, max: 3 }, { min: -10, max: 10 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: 1, max: 3 }, { min: -10, max: -5 }]; |
||||
expected = [{ min: -3, max: 3 }, { min: -10, max: 10 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
}); |
||||
|
||||
describe('both across zero', () => { |
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: -10, max: 5 }, { min: -2, max: 3 }]; |
||||
expected = [{ min: -10, max: 15 }, { min: -2, max: 3 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: -5, max: 10 }, { min: -3, max: 2 }]; |
||||
expected = [{ min: -15, max: 10 }, { min: -3, max: 2 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
}); |
||||
|
||||
describe('one of graphs on zero', () => { |
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: 0, max: 3 }, { min: 5, max: 10 }]; |
||||
expected = [{ min: 0, max: 3 }, { min: 0, max: 10 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: 5, max: 10 }, { min: 0, max: 3 }]; |
||||
expected = [{ min: 0, max: 10 }, { min: 0, max: 3 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: -3, max: 0 }, { min: -10, max: -5 }]; |
||||
expected = [{ min: -3, max: 0 }, { min: -10, max: 0 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: -10, max: -5 }, { min: -3, max: 0 }]; |
||||
expected = [{ min: -10, max: 0 }, { min: -3, max: 0 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
}); |
||||
|
||||
describe('both graphs on zero', () => { |
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: 0, max: 3 }, { min: -10, max: 0 }]; |
||||
expected = [{ min: -3, max: 3 }, { min: -10, max: 10 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: -3, max: 0 }, { min: 0, max: 10 }]; |
||||
expected = [{ min: -3, max: 3 }, { min: -10, max: 10 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
}); |
||||
|
||||
describe('mixed placement of graphs relative to zero', () => { |
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: -10, max: 5 }, { min: 1, max: 3 }]; |
||||
expected = [{ min: -10, max: 5 }, { min: -6, max: 3 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: 1, max: 3 }, { min: -10, max: 5 }]; |
||||
expected = [{ min: -6, max: 3 }, { min: -10, max: 5 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: -10, max: 5 }, { min: -3, max: -1 }]; |
||||
expected = [{ min: -10, max: 5 }, { min: -3, max: 1.5 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axes', () => { |
||||
yaxes = [{ min: -3, max: -1 }, { min: -10, max: 5 }]; |
||||
expected = [{ min: -3, max: 1.5 }, { min: -10, max: 5 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
}); |
||||
|
||||
describe('on level not zero', () => { |
||||
it('Should shrink Y axis', () => { |
||||
alignY = 1; |
||||
yaxes = [{ min: 5, max: 10 }, { min: 2, max: 4 }]; |
||||
expected = [{ min: 4, max: 10 }, { min: 2, max: 4 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axes', () => { |
||||
alignY = 2; |
||||
yaxes = [{ min: -3, max: 1 }, { min: 5, max: 10 }]; |
||||
expected = [{ min: -3, max: 7 }, { min: -6, max: 10 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axes', () => { |
||||
alignY = -1; |
||||
yaxes = [{ min: -5, max: 5 }, { min: -2, max: 3 }]; |
||||
expected = [{ min: -5, max: 15 }, { min: -2, max: 3 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
|
||||
it('Should shrink Y axes', () => { |
||||
alignY = -2; |
||||
yaxes = [{ min: -2, max: 3 }, { min: 5, max: 10 }]; |
||||
expected = [{ min: -2, max: 3 }, { min: -2, max: 10 }]; |
||||
|
||||
alignYLevel(yaxes, alignY); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
}); |
||||
|
||||
describe('on level not number value', () => { |
||||
it('Should ignore without errors', () => { |
||||
yaxes = [{ min: 5, max: 10 }, { min: 2, max: 4 }]; |
||||
expected = [{ min: 5, max: 10 }, { min: 2, max: 4 }]; |
||||
|
||||
alignYLevel(yaxes, 'q'); |
||||
expect(yaxes).toMatchObject(expected); |
||||
}); |
||||
}); |
||||
}); |
Loading…
Reference in new issue