From cc384c7c4cafbf556817244dbf17f634777e1608 Mon Sep 17 00:00:00 2001 From: AshishMotanamGurunadham Date: Fri, 9 Oct 2020 14:47:31 -0500 Subject: [PATCH] New: Timeline API x-axis y-coordinates property(fixes #238) --- docs/controls/Timeline.md | 1 + src/main/js/controls/Timeline/Timeline.js | 10 ++- .../js/controls/Timeline/TimelineConfig.js | 9 ++ .../Timeline/helpers/creationHelpers.js | 32 +++++-- .../Timeline/helpers/translateHelpers.js | 6 +- src/main/js/helpers/errors.js | 2 + src/main/js/helpers/label.js | 2 +- .../controls/Timeline/TimelineLoad-spec.js | 89 +++++++++++++++++++ 8 files changed, 139 insertions(+), 12 deletions(-) diff --git a/docs/controls/Timeline.md b/docs/controls/Timeline.md index 7f7824ccf..e1e0617d1 100644 --- a/docs/controls/Timeline.md +++ b/docs/controls/Timeline.md @@ -100,6 +100,7 @@ timelineDefault.loadContent(/* Data array D */); | showLabel | boolean | true | Toggle to show X axis label | | showLegend | boolean | true | Toggle to show graph legend | | throttle | number | (1000/60) => time in ms | Delay between resize of a browser window | +| axisYPosition | number | undefined | Sets the position of axis in y direction | ### Axis diff --git a/src/main/js/controls/Timeline/Timeline.js b/src/main/js/controls/Timeline/Timeline.js index c279c5009..4d16a0e58 100644 --- a/src/main/js/controls/Timeline/Timeline.js +++ b/src/main/js/controls/Timeline/Timeline.js @@ -55,9 +55,13 @@ const setCanvasWidth = (container, config) => { * @returns {undefined} - returns nothing */ const setCanvasHeight = (config) => { - config.canvasHeight = - getYAxisHeight(config) + - (config.padding.bottom * 2 + config.padding.top) * 2; + config.canvasHeight = config.axisYPosition + ? getYAxisHeight(config) + + (config.padding.bottom * 2 + config.padding.top) * 2 + + config.axisYPosition + : getYAxisHeight(config) + + (config.padding.bottom * 2 + config.padding.top) * 2; + return config.canvasHeight; }; /** * Data point sets can be loaded using this function. diff --git a/src/main/js/controls/Timeline/TimelineConfig.js b/src/main/js/controls/Timeline/TimelineConfig.js index a8be55658..260d78c2c 100644 --- a/src/main/js/controls/Timeline/TimelineConfig.js +++ b/src/main/js/controls/Timeline/TimelineConfig.js @@ -81,6 +81,7 @@ export const processInput = (input, config) => { rangeRounding: getDefaultValue(_axis.x.rangeRounding, true) }); config.shownTargets = []; + config.axisYPosition = input.axisYPosition; return config; }; @@ -170,6 +171,14 @@ class TimelineConfig extends BaseConfig { ) { throw new Error(errors.THROW_MSG_INVALID_AXIS_TYPE_VALUES); } + if ( + utils.isDefined(this.input.axisYPosition) && + this.input.axisYPosition < 0 + ) { + throw new Error( + errors.THROW_MSG_AXIS_Y_POSITION_CANNOT_BE_NEGATIVE + ); + } return this; } diff --git a/src/main/js/controls/Timeline/helpers/creationHelpers.js b/src/main/js/controls/Timeline/helpers/creationHelpers.js index 1b893796a..f69ac2ffa 100644 --- a/src/main/js/controls/Timeline/helpers/creationHelpers.js +++ b/src/main/js/controls/Timeline/helpers/creationHelpers.js @@ -29,6 +29,7 @@ import { getShapeForTarget } from "../../Graph/helpers/helpers"; import { transformPoint } from "./translateHelpers"; +import { getDefaultValue } from "../../../core/BaseConfig"; /** * @typedef TimelineContent @@ -119,10 +120,28 @@ const getXAxisLabelXPosition = (config) => * @param {object} config - config object derived from input JSON * @returns {number} Position for the label */ -const getXAxisLabelYPosition = (config) => - getXAxisYPosition(config) + - config.axisLabelHeights.x * 2 + - config.padding.bottom * 4; +const getXAxisLabelYPosition = (config) => { + const defaultLabelPosition = + getXAxisYPosition(config) + + config.axisLabelHeights.x * 2 + + config.padding.bottom * 4; + if ( + utils.isUndefined(config.axisYPosition) || + defaultLabelPosition > + getXAxisYPosition(config) + + config.axisLabelHeights.x * 2 + + config.axisYPosition + ) { + return defaultLabelPosition; + } else { + return ( + getXAxisYPosition(config) + + config.axisLabelHeights.x * 2 + + config.axisYPosition + + config.padding.bottom * 3 + ); + } +}; /** * Prepares X,Y and Y2 Axes according to their scale and available container width and height * @@ -211,8 +230,9 @@ const createAxes = (axis, scale, config, canvasSVG) => { .attr("aria-hidden", false) .attr( "transform", - `translate(${getXAxisXPosition(config)}, ${getXAxisYPosition( - config + `translate(${getXAxisXPosition(config)}, ${getDefaultValue( + config.axisYPosition, + getXAxisYPosition(config) )})` ) .call(axis.x); diff --git a/src/main/js/controls/Timeline/helpers/translateHelpers.js b/src/main/js/controls/Timeline/helpers/translateHelpers.js index 7f687a1ae..c48a5e6c6 100644 --- a/src/main/js/controls/Timeline/helpers/translateHelpers.js +++ b/src/main/js/controls/Timeline/helpers/translateHelpers.js @@ -13,6 +13,7 @@ import { getXAxisXPosition, getXAxisYPosition } from "./creationHelpers"; +import { getDefaultValue } from "../../../core/BaseConfig"; /** * Updates clipPath rectangle width and height on resize. @@ -48,8 +49,9 @@ const translateAxes = (axis, scale, config, canvasSVG) => { .call(constants.d3Transition(config.settingsDictionary.transition)) .attr( "transform", - `translate(${getXAxisXPosition(config)},${getXAxisYPosition( - config + `translate(${getXAxisXPosition(config)},${getDefaultValue( + config.axisYPosition, + getXAxisYPosition(config) )})` ) .call(axis.x); diff --git a/src/main/js/helpers/errors.js b/src/main/js/helpers/errors.js index 92c603be4..eab553917 100644 --- a/src/main/js/helpers/errors.js +++ b/src/main/js/helpers/errors.js @@ -48,6 +48,8 @@ export default { THROW_MSG_UNIQUE_LABEL_NOT_PROVIDED: "Invalid input format, unique label must be provided.", THROW_MSG_NO_CONTENT_DATA_LOADED: "Content data needs to be loaded.", + THROW_MSG_AXIS_Y_POSITION_CANNOT_BE_NEGATIVE: + "Axis Y position cannot be negative.", /** * @description Axes */ diff --git a/src/main/js/helpers/label.js b/src/main/js/helpers/label.js index ec39bfc2e..f1a0f28e6 100644 --- a/src/main/js/helpers/label.js +++ b/src/main/js/helpers/label.js @@ -339,7 +339,7 @@ const createTooltipDiv = () => { const destroyTooltipDiv = () => { if (document.querySelector(`.${styles.labelPopupTooltip}`)) { const element = document.querySelector(`.${styles.labelPopupTooltip}`); - if(element.parentNode) { + if (element.parentNode) { element.parentNode.removeChild(element); } } diff --git a/src/test/unit/controls/Timeline/TimelineLoad-spec.js b/src/test/unit/controls/Timeline/TimelineLoad-spec.js index c3a59da83..9b5b909b2 100644 --- a/src/test/unit/controls/Timeline/TimelineLoad-spec.js +++ b/src/test/unit/controls/Timeline/TimelineLoad-spec.js @@ -27,6 +27,7 @@ import { secondaryValuesJSON, valuesJSON } from "./helpers"; +import { getSVGAnimatedTransformList } from "../../../../main/js/helpers/transformUtils"; describe("Timeline - Load", () => { let input; @@ -127,6 +128,20 @@ describe("Timeline - Load", () => { timeline.loadContent(getData(valuesJSON)); }).toThrowError(errors.THROW_MSG_UNIQUE_KEY_NOT_PROVIDED); }); + it("Throws error for when axisYPosition value is less than 0", () => { + expect(() => { + timeline = new Timeline( + Object.assign( + { + axisYPosition: -1 + }, + getAxes(axisJSON) + ) + ); + }).toThrowError( + errors.THROW_MSG_AXIS_Y_POSITION_CANNOT_BE_NEGATIVE + ); + }); }); it("Clones the input object correctly", () => { expect(timeline.contentConfig[0].config.key).toBe(input.key); @@ -213,6 +228,80 @@ describe("Timeline - Load", () => { (config.padding.top + config.padding.bottom) * 2 ); }); + describe("When axisYPosition is provided", () => { + beforeEach(() => { + timeline.destroy(); + input = getData(valuesJSON); + const config = getAxes(axisJSON); + timeline = new Timeline( + Object.assign( + { + axisYPosition: 10 + }, + config + ) + ); + timeline.loadContent(input); + }); + it("check position of axis in container", () => { + const axisGroup = document.querySelector(`.${styles.axisX}`); + const translate = getSVGAnimatedTransformList( + axisGroup.getAttribute("transform") + ).translate; + expect(translate[1]).toEqual(timeline.config.axisYPosition); + }); + it("check height of graph container", () => { + const canvas = d3.select(`.${styles.canvas}`); + const canvasHeight = + getYAxisHeight(timeline.config) + + (timeline.config.padding.bottom * 2 + + timeline.config.padding.top) * + 2 + + timeline.config.axisYPosition; + expect(toNumber(canvas.attr("height"))).toEqual(canvasHeight); + }); + describe("check position of label", () => { + it(" when axisYPosition is small", () => { + const labelGroup = document.querySelector( + `.${styles.axisLabelX}` + ); + const translate = getSVGAnimatedTransformList( + labelGroup.getAttribute("transform") + ).translate; + expect(translate[1]).toEqual( + getXAxisYPosition(timeline.config) + + timeline.config.axisLabelHeights.x * 2 + + timeline.config.padding.bottom * 4 + ); + }); + it(" when axisYPosition is large", () => { + timeline.destroy(); + input = getData(valuesJSON); + const config = getAxes(axisJSON); + timeline = new Timeline( + Object.assign( + { + axisYPosition: 100 + }, + config + ) + ); + timeline.loadContent(input); + const labelGroup = document.querySelector( + `.${styles.axisLabelX}` + ); + const translate = getSVGAnimatedTransformList( + labelGroup.getAttribute("transform") + ).translate; + expect(translate[1]).toEqual( + getXAxisYPosition(timeline.config) + + timeline.config.axisLabelHeights.x * 2 + + timeline.config.padding.bottom * 3 + + timeline.config.axisYPosition + ); + }); + }); + }); describe("Draws the graph", () => { let input = null; let secondaryInput = null;