From 09d4d0bb192cdbd4a031d8d6d6c440cd76d04a28 Mon Sep 17 00:00:00 2001 From: NarasimhaShenoi <53594676+NarasimhaShenoi@users.noreply.github.com> Date: Tue, 13 Aug 2019 19:29:49 +0530 Subject: [PATCH] Update: Display Data Series In Legend when No Data Exists (#62) * Carbon-42 Display Data Series In Legend when No Data Exists * Update: Display Data Series In Legend when No Data Exists * Update: Display Data Series In Legend when No Data Exists * Added unit testcaes * updated testcases --- .../controls/PairedResult/helpers/helpers.js | 3 +- src/main/js/core/BaseConfig/helper.js | 2 +- src/main/js/helpers/legend.js | 10 +++-- src/main/js/helpers/utils.js | 9 ++++ src/test/unit/controls/Bar/Bar-spec.js | 25 ++++++++++- src/test/unit/controls/Line/Line-spec.js | 43 ++++++++++++++++++- src/test/unit/controls/Line/helpers.js | 2 +- .../PairedResult/PairedResult-spec.js | 27 +++++++++++- .../unit/controls/PairedResult/helpers.js | 2 +- 9 files changed, 112 insertions(+), 11 deletions(-) diff --git a/src/main/js/controls/PairedResult/helpers/helpers.js b/src/main/js/controls/PairedResult/helpers/helpers.js index eae94d794..29ee382bf 100644 --- a/src/main/js/controls/PairedResult/helpers/helpers.js +++ b/src/main/js/controls/PairedResult/helpers/helpers.js @@ -569,7 +569,8 @@ const prepareLegendItems = (config, eventHandlers, dataTarget, legendSVG) => { shape: getValue(d.shape, type), color: getValue(d.color, type), label: getValue(d.label, type), - key: `${d.key}_${type}` + key: `${d.key}_${type}`, + values: dataTarget.values } ); if (dataTarget.label && legendSVG) { diff --git a/src/main/js/core/BaseConfig/helper.js b/src/main/js/core/BaseConfig/helper.js index 15f50352c..0132914ed 100644 --- a/src/main/js/core/BaseConfig/helper.js +++ b/src/main/js/core/BaseConfig/helper.js @@ -29,7 +29,7 @@ export const validateBaseInput = (input) => { if (utils.isEmpty(input.key)) { throw new Error(errors.THROW_MSG_UNIQUE_KEY_NOT_PROVIDED); } - if (utils.isEmpty(input.values)) { + if (!utils.isArray(input.values)) { throw new Error(errors.THROW_MSG_NO_DATA_POINTS); } }; diff --git a/src/main/js/helpers/legend.js b/src/main/js/helpers/legend.js index ca0572083..b297c699d 100644 --- a/src/main/js/helpers/legend.js +++ b/src/main/js/helpers/legend.js @@ -70,15 +70,17 @@ const loadLegendItem = (legendSVG, t, shownTargets, eventHandlers) => { validateLegendLabel(t.label); const text = getText(t.label.display); const index = shownTargets.indexOf(t.key); + const shouldForceDisableLegendItem = + !!t.label.isDisabled || utils.isEmptyArray(t.values); const itemPath = legendSVG .append("li") .classed(styles.legendItem, true) - .attr("aria-selected", index > -1) - .attr("aria-disabled", !!t.label.isDisabled) + .attr("aria-selected", shouldForceDisableLegendItem || index > -1) + .attr("aria-disabled", shouldForceDisableLegendItem) .attr("role", "listitem") .attr("aria-labelledby", text) .attr("aria-describedby", t.key); - if (!t.label.isDisabled && index > -1) { + if (!shouldForceDisableLegendItem && index > -1) { itemPath .on("click", function() { return eventHandlers.clickHandler(this, t); @@ -93,7 +95,7 @@ const loadLegendItem = (legendSVG, t, shownTargets, eventHandlers) => { itemPath .append("button") .classed(styles.legendItemBtn, true) - .attr("tabindex", t.label.isDisabled ? -1 : 0) + .attr("tabindex", shouldForceDisableLegendItem ? -1 : 0) .append(() => new Shape(getShapeForTarget(t)).getShapeElement( getDefaultSVGProps({ diff --git a/src/main/js/helpers/utils.js b/src/main/js/helpers/utils.js index b0871e04b..b65a5fe76 100644 --- a/src/main/js/helpers/utils.js +++ b/src/main/js/helpers/utils.js @@ -29,6 +29,14 @@ const isFunction = (o) => typeof o === "function"; * @returns {boolean} true if Array */ const isArray = (o) => Array.isArray(o); +/** + * Checks if parameter is an Array and it is having zero elements + * + * @private + * @param {object} o - source object + * @returns {boolean} true if it is an Array and length is zero + */ +const isEmptyArray = (o) => Array.isArray(o) && o.length === 0; /** * Checks if parameter is a String * @@ -197,6 +205,7 @@ export default { deepClone, isFunction, isArray, + isEmptyArray, isDefined, isUndefined, isEmpty, diff --git a/src/test/unit/controls/Bar/Bar-spec.js b/src/test/unit/controls/Bar/Bar-spec.js index 8ce57bbc0..33bd072f4 100644 --- a/src/test/unit/controls/Bar/Bar-spec.js +++ b/src/test/unit/controls/Bar/Bar-spec.js @@ -70,11 +70,34 @@ describe("Bar", () => { }); it("throws error when no values are provided", () => { const input = utils.deepClone(getInput(valuesDefault)); - input.values = []; + input.values = undefined; expect(() => { graphDefault.loadContent(new Bar(input)); }).toThrowError(errors.THROW_MSG_NO_DATA_POINTS); }); + it("does not throw error when empty array is provided", () => { + const input = utils.deepClone(getInput(valuesDefault)); + input.values = []; + expect(() => { + graphDefault.loadContent(new Bar(input)); + }).not.toThrow(); + }); + it("display the legend when empty array is provided as input", () => { + graphDefault.loadContent(new Bar(getInput([]))); + const legendContainer = fetchElementByClass( + barGraphContainer, + styles.legend + ); + const legendItems = legendContainer.children; + expect(legendContainer).not.toBeNull(); + expect(legendContainer.tagName).toBe("UL"); + expect(legendItems.length).toBe(1); + const legendItem = document.body.querySelector( + `.${styles.legendItem}` + ); + expect(legendItem.getAttribute("aria-disabled")).toBe("true"); + expect(legendItem.getAttribute("aria-selected")).toBe("true"); + }); it("throws error when no ticks are provided for x-axis", () => { const axisData = utils.deepClone(getAxes(axisDefault)); axisData.axis.x.ticks = {}; diff --git a/src/test/unit/controls/Line/Line-spec.js b/src/test/unit/controls/Line/Line-spec.js index d49fd8a53..124f6b51e 100644 --- a/src/test/unit/controls/Line/Line-spec.js +++ b/src/test/unit/controls/Line/Line-spec.js @@ -70,9 +70,34 @@ describe("Line", () => { }); it("throws error when no values are provided", () => { expect(() => { - graphDefault.loadContent(new Line(getInput([], false, false))); + graphDefault.loadContent( + new Line(getInput(undefined, false, false)) + ); }).toThrowError(errors.THROW_MSG_NO_DATA_POINTS); }); + it("display the legend when values are provided", () => { + const input = getInput(valuesDefault); + graphDefault.loadContent(new Line(input)); + const legendContainer = fetchElementByClass( + lineGraphContainer, + styles.legend + ); + const legendItems = legendContainer.children; + expect(legendContainer).not.toBeNull(); + expect(legendContainer.tagName).toBe("UL"); + expect(legendItems.length).toBe(1); + const legendItem = document.body.querySelector( + `.${styles.legendItem}` + ); + expect(legendItem.getAttribute("aria-disabled")).toBe("false"); + }); + it("does not throw error when empty array is provided", () => { + const input = utils.deepClone(getInput(valuesDefault)); + input.values = []; + expect(() => { + graphDefault.loadContent(new Line(input)); + }).not.toThrow(); + }); it("does not throw error when datetime values have milliseconds", () => { expect(() => { const graphTimeSeries = new Graph(getAxes(axisTimeSeries)); @@ -908,6 +933,22 @@ describe("Line", () => { }); }); describe("prepares to load legend item", () => { + it("display the legend when empty array is provided as input", () => { + graphDefault.loadContent(new Line(getInput([], false, true))); + const legendContainer = fetchElementByClass( + lineGraphContainer, + styles.legend + ); + const legendItems = legendContainer.children; + expect(legendContainer).not.toBeNull(); + expect(legendContainer.tagName).toBe("UL"); + expect(legendItems.length).toBe(1); + const legendItem = document.body.querySelector( + `.${styles.legendItem}` + ); + expect(legendItem.getAttribute("aria-disabled")).toBe("true"); + expect(legendItem.getAttribute("aria-selected")).toBe("true"); + }); it("does not load if legend is opted to be hidden", () => { graphDefault.destroy(); const input = getAxes(axisDefault); diff --git a/src/test/unit/controls/Line/helpers.js b/src/test/unit/controls/Line/helpers.js index 22ba1b26f..85c7760aa 100644 --- a/src/test/unit/controls/Line/helpers.js +++ b/src/test/unit/controls/Line/helpers.js @@ -17,7 +17,7 @@ export const dataPointClickHandlerSpy = sinon.spy(); * @returns {object} input JSON */ export const getInput = ( - values = [], + values, isDefaultColor = true, isDefaultShape = true, isY2Axis = false diff --git a/src/test/unit/controls/PairedResult/PairedResult-spec.js b/src/test/unit/controls/PairedResult/PairedResult-spec.js index 4400c0fc5..bc9cc1dc2 100644 --- a/src/test/unit/controls/PairedResult/PairedResult-spec.js +++ b/src/test/unit/controls/PairedResult/PairedResult-spec.js @@ -75,10 +75,35 @@ describe("PairedResult", () => { it("throws error when no values are provided", () => { expect(() => { graphDefault.loadContent( - new PairedResult(getInput([], false, false)) + new PairedResult(getInput(undefined, false, false)) ); }).toThrowError(errors.THROW_MSG_NO_DATA_POINTS); }); + it("does not throw error when empty array is provided", () => { + const input = utils.deepClone(getInput(valuesDefault)); + input.values = []; + expect(() => { + graphDefault.loadContent(new PairedResult(input)); + }).not.toThrow(); + }); + it("display the legend when empty array is provided as input", () => { + const input = utils.deepClone(getInput(valuesDefault)); + input.values = []; + graphDefault.loadContent(new PairedResult(input)); + const legendContainer = fetchElementByClass( + pairedResultGraphContainer, + styles.legend + ); + const legendItems = legendContainer.children; + expect(legendContainer).not.toBeNull(); + expect(legendContainer.tagName).toBe("UL"); + expect(legendItems.length).toBe(3); + const legendItem = document.body.querySelector( + `.${styles.legendItem}` + ); + expect(legendItem.getAttribute("aria-disabled")).toBe("true"); + expect(legendItem.getAttribute("aria-selected")).toBe("true"); + }); it("does not throw error when datetime values have milliseconds", () => { expect(() => { const graphTimeSeries = new Graph(getAxes(axisTimeSeries)); diff --git a/src/test/unit/controls/PairedResult/helpers.js b/src/test/unit/controls/PairedResult/helpers.js index 1ad679d5b..e25ecef6e 100644 --- a/src/test/unit/controls/PairedResult/helpers.js +++ b/src/test/unit/controls/PairedResult/helpers.js @@ -17,7 +17,7 @@ export const dataPointClickHandlerSpy = sinon.spy(); * @returns {object} input JSON */ export const getInput = ( - values = [], + values, isDefaultColor = true, isDefaultShape = true, isY2Axis = false