From 1c743804273955dd9c515dcc58d3ff00c67d230b Mon Sep 17 00:00:00 2001 From: KiaraEdTech Date: Tue, 16 Feb 2021 18:13:13 +0100 Subject: [PATCH 1/4] feature stacked bar values on bars (including a fixture) --- chartTypes/bar-stacked/mapping.js | 139 ++++++++++++++++++ .../bar-stacked-mixed-values-on-bars.json | 73 +++++++++ routes/fixtures/data.js | 1 + 3 files changed, 213 insertions(+) create mode 100644 resources/fixtures/data/bar-stacked-mixed-values-on-bars.json diff --git a/chartTypes/bar-stacked/mapping.js b/chartTypes/bar-stacked/mapping.js index a5d08fc7..6e5963c3 100644 --- a/chartTypes/bar-stacked/mapping.js +++ b/chartTypes/bar-stacked/mapping.js @@ -2,6 +2,10 @@ const objectPath = require("object-path"); const Decimal = require("decimal.js"); const clone = require("clone"); const dataHelpers = require("../../helpers/data.js"); +const d3config = require("../../config/d3.js"); +const d3format = require("d3-format"); +const locale = d3format.formatLocale(d3config.formatLocale); +const format = locale.format(d3config.specifier); const intervals = require("../../helpers/dateSeries.js").intervals; @@ -83,6 +87,10 @@ module.exports = function getMapping() { xIndex: rowIndex, yValue: value, cValue: index, + labelWidth: textMeasure.getLabelTextWidth( + format(value), + mappingData.toolRuntimeConfig + ) }; }); }) @@ -181,6 +189,137 @@ module.exports = function getMapping() { objectPath.set(spec, "axes.0.values", [0, 25, 50, 75, 100]); }, }, + + { + path: "item.options.annotations.valuesOnBars", + mapToSpec: function(valuesOnBars, spec, mappingData) { + if (!valuesOnBars) { + return; + } + + const valuePadding = 4; + const tests = { + zeroValue: "datum.datum.yValue == 0", + positiveValue: "datum.datum.yValue > 0", + negativeValue: "datum.datum.yValue < 0", + labelTooBig: `datum.width < datum.datum.labelWidth + ${valuePadding * 2}`, + contrastFineForDark: `contrast('${mappingData.toolRuntimeConfig.text.fill}', datum.fill) > contrast('white', datum.fill)` + }; + + const valueLabelMark = { + type: "text", + from: { + data: "bar" + }, + encode: { + enter: { + y: { + field: "y" + }, + dy: { + signal: "barWidth / 2" + }, + baseline: { + value: "middle" + }, + opacity: [ + { + test: tests.zeroValue, + value: 0 + }, + { + test: tests.labelTooBig, + value: 0 + } + ], + x: [ + { + test: tests.positiveValue, + field: "x" + }, + { + test: tests.negativeValue, + field: "x2" + }, + { + field: "x" + } + ], + align: [ + { + test: tests.positiveValue, + value: "left" + }, + { + test: tests.negativeValue, + value: "right" + }, + { + value: "left" + } + ], + dx: [ + { + test: tests.positiveValue, + value: valuePadding + }, + { + test: tests.negativeValue, + value: -valuePadding + }, + { + value: valuePadding + } + ], + fill: [ + { + test: tests.contrastFineForDark, + value: mappingData.toolRuntimeConfig.text.fill + }, + { + value: "white" + } + ], + text: { + signal: `format(datum.datum.yValue, "${d3config.specifier}")` + } + } + } + }; + + // add the value label marks + spec.marks[0].marks[0].marks.push(valueLabelMark); + + /* + objectPath.set(spec, "axes.0.grid", false); + objectPath.set(spec, "axes.0.ticks", false); + objectPath.set(spec, "axes.0.domain", false); + objectPath.set(spec, "axes.0.labels", false); + + // the grid and the ticks of the y axis should get hidden + // the labels follow any settings handled before + objectPath.set(spec, "axes.1.grid", false); + objectPath.set(spec, "axes.1.ticks", false); + + // keep the 0 tick line only + // hide the domain + // do not show labels + objectPath.set(spec, "axes.0.grid", true); + objectPath.set( + spec, + "axes.0.gridColor", + mappingData.toolRuntimeConfig.axis.labelColor + ); + + objectPath.set(spec, "axes.0.values", [0]); + + // make sure the axis is drawn on top, so it's in front of positive and negative bars + objectPath.set(spec, "axes.0.zindex", 1);*/ + + } + + }, + { path: "item.options.hideAxisLabel", mapToSpec: function (hideAxisLabel, spec) { diff --git a/resources/fixtures/data/bar-stacked-mixed-values-on-bars.json b/resources/fixtures/data/bar-stacked-mixed-values-on-bars.json new file mode 100644 index 00000000..d064120c --- /dev/null +++ b/resources/fixtures/data/bar-stacked-mixed-values-on-bars.json @@ -0,0 +1,73 @@ +{ + "title": "FIXTURE: StackedBar - mixed values - values on bars", + "data": [ + ["Jahr", "Data 1", "Data 2", "Data 3", "Data 4", "Data 5", "Data 6"], + ["2010", "100", "24", "0", "5", "2", null], + ["2020", "45", "35", "3", null, "1", "1"], + ["2030", "-21", null, "-56", "16", "1", "3"] + ], + "allowDownloadData": false, + "sources": [ + { + "link": { + "url": "https://nzz.ch/visuals", + "isValid": true + }, + "text": "Some source" + } + ], + "options": { + "chartType": "StackedBar", + "hideAxisLabel": false, + "annotations": { + "valuesOnBars": true + }, + "barOptions": { + "isBarChart": true, + "forceBarsOnSmall": false + }, + "dateSeriesOptions": { + "interval": "year", + "prognosisStart": null + }, + "lineChartOptions": { + "reverseYScale": false, + "lineInterpolation": "linear", + "isStockChart": false + }, + "dotplotOptions": {}, + "arrowOptions": { + "colorScheme": 0 + }, + "highlightDataSeries": [], + "colorOverwritesSeries": [ + { + "color": "#0084c7", + "colorLight": "#000000", + "position": 5 + }, + { + "color": "#408b3d", + "position": 6 + }, + { + "color": "#c31906", + "position": 2 + }, + { + "color": "#da467d", + "position": 3 + }, + { + "color": "#d28b00", + "position": 4 + }, + { + "color": "#54ba00", + "position": 1 + } + ] + }, + "subtitle": "Some subtitle", + "notes": "Some longer description or notes to the data shown above." +} diff --git a/routes/fixtures/data.js b/routes/fixtures/data.js index 45c737ad..12bca20f 100644 --- a/routes/fixtures/data.js +++ b/routes/fixtures/data.js @@ -32,6 +32,7 @@ const fixtureData = [ require(`${fixtureDataDirectory}/bar-stacked-qualitative-negative-only.json`), require(`${fixtureDataDirectory}/bar-stacked-qualitative-negative.json`), require(`${fixtureDataDirectory}/bar-stacked-null-values.json`), + require(`${fixtureDataDirectory}/bar-stacked-mixed-values-on-bars.json`), require(`${fixtureDataDirectory}/bar-values-on-bars.json`), require(`${fixtureDataDirectory}/bar-many-rows.json`), require(`${fixtureDataDirectory}/bar-many-rows-values-on-bars.json`), From 24aa9171fa85b00ea805e8c3e92e9ff661861295 Mon Sep 17 00:00:00 2001 From: KiaraEdTech Date: Wed, 17 Feb 2021 10:02:15 +0100 Subject: [PATCH 2/4] format & removed unnecessary commented code --- chartTypes/bar-stacked/mapping.js | 104 ++++++++++++------------------ 1 file changed, 41 insertions(+), 63 deletions(-) diff --git a/chartTypes/bar-stacked/mapping.js b/chartTypes/bar-stacked/mapping.js index 6e5963c3..28deed38 100644 --- a/chartTypes/bar-stacked/mapping.js +++ b/chartTypes/bar-stacked/mapping.js @@ -70,7 +70,10 @@ module.exports = function getMapping() { } // check if we need to shorten the number labels - const divisor = dataHelpers.getDivisor(itemData, mappingData.item.options.largeNumbers); + const divisor = dataHelpers.getDivisor( + itemData, + mappingData.item.options.largeNumbers + ); spec.data[0].values = clone(itemData) .slice(1) // take the header row out of the array @@ -90,7 +93,7 @@ module.exports = function getMapping() { labelWidth: textMeasure.getLabelTextWidth( format(value), mappingData.toolRuntimeConfig - ) + ), }; }); }) @@ -189,10 +192,9 @@ module.exports = function getMapping() { objectPath.set(spec, "axes.0.values", [0, 25, 50, 75, 100]); }, }, - { path: "item.options.annotations.valuesOnBars", - mapToSpec: function(valuesOnBars, spec, mappingData) { + mapToSpec: function (valuesOnBars, spec, mappingData) { if (!valuesOnBars) { return; } @@ -202,124 +204,97 @@ module.exports = function getMapping() { zeroValue: "datum.datum.yValue == 0", positiveValue: "datum.datum.yValue > 0", negativeValue: "datum.datum.yValue < 0", - labelTooBig: `datum.width < datum.datum.labelWidth + ${valuePadding * 2}`, - contrastFineForDark: `contrast('${mappingData.toolRuntimeConfig.text.fill}', datum.fill) > contrast('white', datum.fill)` + labelTooBig: `datum.width < datum.datum.labelWidth + ${ + valuePadding * 2 + }`, + contrastFineForDark: `contrast('${mappingData.toolRuntimeConfig.text.fill}', datum.fill) > contrast('white', datum.fill)`, }; const valueLabelMark = { type: "text", from: { - data: "bar" + data: "bar", }, encode: { enter: { y: { - field: "y" + field: "y", }, dy: { - signal: "barWidth / 2" + signal: "barWidth / 2", }, baseline: { - value: "middle" + value: "middle", }, opacity: [ { test: tests.zeroValue, - value: 0 + value: 0, }, { test: tests.labelTooBig, - value: 0 - } + value: 0, + }, ], x: [ { test: tests.positiveValue, - field: "x" + field: "x", }, { test: tests.negativeValue, - field: "x2" + field: "x2", }, { - field: "x" - } + field: "x", + }, ], align: [ { test: tests.positiveValue, - value: "left" + value: "left", }, { test: tests.negativeValue, - value: "right" + value: "right", }, { - value: "left" - } + value: "left", + }, ], dx: [ { test: tests.positiveValue, - value: valuePadding + value: valuePadding, }, { test: tests.negativeValue, - value: -valuePadding + value: -valuePadding, }, { - value: valuePadding - } + value: valuePadding, + }, ], fill: [ { test: tests.contrastFineForDark, - value: mappingData.toolRuntimeConfig.text.fill + value: mappingData.toolRuntimeConfig.text.fill, }, { - value: "white" - } + value: "white", + }, ], text: { - signal: `format(datum.datum.yValue, "${d3config.specifier}")` - } - } - } + signal: `format(datum.datum.yValue, "${d3config.specifier}")`, + }, + }, + }, }; // add the value label marks spec.marks[0].marks[0].marks.push(valueLabelMark); - - /* - objectPath.set(spec, "axes.0.grid", false); - objectPath.set(spec, "axes.0.ticks", false); - objectPath.set(spec, "axes.0.domain", false); - objectPath.set(spec, "axes.0.labels", false); - - // the grid and the ticks of the y axis should get hidden - // the labels follow any settings handled before - objectPath.set(spec, "axes.1.grid", false); - objectPath.set(spec, "axes.1.ticks", false); - - // keep the 0 tick line only - // hide the domain - // do not show labels - objectPath.set(spec, "axes.0.grid", true); - objectPath.set( - spec, - "axes.0.gridColor", - mappingData.toolRuntimeConfig.axis.labelColor - ); - - objectPath.set(spec, "axes.0.values", [0]); - - // make sure the axis is drawn on top, so it's in front of positive and negative bars - objectPath.set(spec, "axes.0.zindex", 1);*/ - - } - + }, }, - { path: "item.options.hideAxisLabel", mapToSpec: function (hideAxisLabel, spec) { @@ -337,7 +312,10 @@ module.exports = function getMapping() { path: "item.options.barOptions.maxValue", mapToSpec: function (maxValue, spec, mappingData) { // check if we need to shorten the number labels - const divisor = dataHelpers.getDivisor(mappingData.item.data, mappingData.item.options.largeNumbers); + const divisor = dataHelpers.getDivisor( + mappingData.item.data, + mappingData.item.options.largeNumbers + ); const dataMaxValue = dataHelpers.getMaxValue(mappingData.item.data); if (dataMaxValue > maxValue) { From 4a506a04b1c35c85b28eae2d0041feefae352deb Mon Sep 17 00:00:00 2001 From: KiaraEdTech Date: Wed, 17 Feb 2021 10:36:13 +0100 Subject: [PATCH 3/4] added option availability --- routes/option-availability.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/routes/option-availability.js b/routes/option-availability.js index 6d4e4f25..a5034feb 100644 --- a/routes/option-availability.js +++ b/routes/option-availability.js @@ -183,6 +183,10 @@ module.exports = { available = true; } + if (isStackedBarChart(item)) { + available = true; + } + if (isDotplot(item)) { available = true; } @@ -229,7 +233,7 @@ module.exports = { if (request.params.optionName === "annotations.valuesOnBars") { try { return { - available: isBarChart(item) + available: isBarChart(item) || isStackedBarChart(item) }; } catch (e) { return { From 87a3ed10bc3072b6fdc9cbba219aa3967e937cf7 Mon Sep 17 00:00:00 2001 From: KiaraEdTech Date: Wed, 17 Feb 2021 14:41:23 +0100 Subject: [PATCH 4/4] incremented version number --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 15fa8134..94053554 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "q-chart", - "version": "3.4.2", + "version": "3.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b9160527..3a26da85 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "q-chart", - "version": "3.4.2", + "version": "3.5.0", "description": "", "main": "index.js", "scripts": {