diff --git a/ui/app/components/clients/horizontal-bar-charts.js b/ui/app/components/clients/horizontal-bar-charts.js index 65561d069303..7e191341f627 100644 --- a/ui/app/components/clients/horizontal-bar-charts.js +++ b/ui/app/components/clients/horizontal-bar-charts.js @@ -1,6 +1,5 @@ import Component from '@glimmer/component'; import { action } from '@ember/object'; -import { assert } from '@ember/debug'; import { stack } from 'd3-shape'; // eslint-disable-next-line no-unused-vars import { select, event, selectAll } from 'd3-selection'; @@ -17,7 +16,7 @@ import { max, maxIndex } from 'd3-array'; * * ``` * @param {object} dataset - dataset for the chart - * @param {array} chartLegend - array of objects with key names 'key' and 'label' for the map legend + * @param {array} chartLegend - array of objects with key names 'key' and 'label' for the chart legend * @param {string} [labelKey=label] - labelKey is the key name in the dataset passed in that corresponds to the value labeling the y-axis (i.e. 'namespace_path') * @param {string} [param1=defaultValue] - param1 is... */ @@ -27,6 +26,7 @@ import { max, maxIndex } from 'd3-array'; // SIZING CONSTANTS const CHART_MARGIN = { top: 10, left: 137 }; // makes space for y-axis legend +const TRANSLATE = { down: 16 }; const CHAR_LIMIT = 18; // character count limit for y-axis labels to trigger truncating const LINE_HEIGHT = 24; // each bar w/ padding is 24 pixels thick @@ -103,28 +103,13 @@ export default class HorizontalBarChart extends Component { } get chartLegend() { - assert( - 'chart legend is required, must be an array of objects with key names of "key" and "label"', - this.hasLegend() - ); return this.args.chartLegend; } - // TODO: use maxIndex function when packages are updated get topNamespace() { - console.log(this.args.dataset[maxIndex(this.args.dataset, d => d.total)]); return this.args.dataset[maxIndex(this.args.dataset, d => d.total)]; } - hasLegend() { - if (!this.args.chartLegend || !Array.isArray(this.args.chartLegend)) { - return false; - } else { - let legendKeys = this.args.chartLegend.map(obj => Object.keys(obj)); - return legendKeys.map(array => array.includes('key', 'label')).every(element => element === true); - } - } - @action renderChart(element, args) { // chart legend tells stackFunction how to stack/organize data @@ -134,6 +119,7 @@ export default class HorizontalBarChart extends Component { let dataset = args[0]; let stackedData = stackFunction(dataset); let labelKey = this.labelKey; + let handleClick = this.args.onClick; let xScale = scaleLinear() .domain([0, max(dataset.map(d => d.total))]) @@ -161,13 +147,14 @@ export default class HorizontalBarChart extends Component { let yAxis = axisLeft(yScale).tickSize(0); yAxis(chartSvg.append('g').attr('transform', `translate(${CHART_MARGIN.left}, ${CHART_MARGIN.top})`)); + chartSvg.select('.domain').remove(); + let truncate = selection => selection.text(string => string.length < CHAR_LIMIT ? string : string.slice(0, CHAR_LIMIT - 3) + '...' ); chartSvg.selectAll('.tick text').call(truncate); - chartSvg.select('.domain').remove(); groups .selectAll('rect') @@ -184,23 +171,147 @@ export default class HorizontalBarChart extends Component { .attr('rx', 3) .attr('ry', 3); - let startingXCoordinate = 100 - this.chartLegend.length * 20; - let legendSvg = select('.legend'); - this.chartLegend.map((legend, i) => { - let xCoordinate = startingXCoordinate + i * 20; - legendSvg - .append('circle') - .attr('cx', `${xCoordinate}%`) - .attr('cy', '50%') - .attr('r', 6) - .style('fill', `${BAR_COLOR_DEFAULT[i]}`); - legendSvg - .append('text') - .attr('x', `${xCoordinate + 2}%`) - .attr('y', '50%') - .text(`${legend.label}`) - .style('font-size', '.8rem') - .attr('alignment-baseline', 'middle'); - }); + let actionBars = chartSvg + .selectAll('.action-bar') + .data(dataset) + .enter() + .append('rect') + .style('cursor', 'pointer') + .attr('class', 'action-bar') + .attr('width', '100%') + .attr('height', `${LINE_HEIGHT}px`) + .attr('x', '0') + .attr('y', chartData => yScale(chartData[labelKey])) + .style('fill', `${BACKGROUND_BAR_COLOR}`) + .style('opacity', '0') + .style('mix-blend-mode', 'multiply'); + + let yLegendBars = chartSvg + .selectAll('.label-bar') + .data(dataset) + .enter() + .append('rect') + .style('cursor', 'pointer') + .attr('class', 'label-action-bar') + .attr('width', CHART_MARGIN.left) + .attr('height', `${LINE_HEIGHT}px`) + .attr('x', '0') + .attr('y', chartData => yScale(chartData[labelKey])) + .style('opacity', '0') + .style('mix-blend-mode', 'multiply'); + + let dataBars = chartSvg.selectAll('rect.data-bar'); + let actionBarSelection = chartSvg.selectAll('rect.action-bar'); + let compareAttributes = (elementA, elementB, attr) => + select(elementA).attr(`${attr}`) === elementB.getAttribute(`${attr}`); + + // MOUSE AND CLICK EVENTS FOR DATA BARS + actionBars + .on('click', function(chartData) { + if (handleClick) { + handleClick(chartData); + } + }) + .on('mouseover', function() { + select(this).style('opacity', 1); + dataBars + .filter(function() { + return compareAttributes(this, event.target, 'y'); + }) + .style('fill', (b, i) => `${BAR_COLOR_HOVER[i]}`); + // TODO: change to use modal instead of tooltip div + select('.chart-tooltip') + .transition() + .duration(200) + .style('opacity', 1); + }) + .on('mouseout', function() { + select(this).style('opacity', 0); + select('.chart-tooltip').style('opacity', 0); + dataBars + .filter(function() { + return compareAttributes(this, event.target, 'y'); + }) + .style('fill', (b, i) => `${BAR_COLOR_DEFAULT[i]}`); + }) + .on('mousemove', function(chartData) { + select('.chart-tooltip') + .style('opacity', 1) + .style('max-width', '200px') + .style('left', `${event.pageX - 325}px`) + .style('top', `${event.pageY - 140}px`) + .text( + `${Math.round((chartData.total * 100) / totalCount)}% of total client counts: + ${chartData.non_entity_tokens} non-entity tokens, ${chartData.distinct_entities} unique entities. + ` + ); + }); + + // MOUSE EVENTS FOR Y-AXIS LABELS + yLegendBars + .on('click', function(chartData) { + if (handleClick) { + handleClick(chartData); + } + }) + .on('mouseover', function(chartData) { + dataBars + .filter(function() { + return compareAttributes(this, event.target, 'y'); + }) + .style('fill', (b, i) => `${BAR_COLOR_HOVER[i]}`); + actionBarSelection + .filter(function() { + return compareAttributes(this, event.target, 'y'); + }) + .style('opacity', '1'); + if (chartData.label.length >= CHAR_LIMIT) { + select('.chart-tooltip') + .transition() + .duration(200) + .style('opacity', 1); + } + }) + .on('mouseout', function() { + select('.chart-tooltip').style('opacity', 0); + dataBars + .filter(function() { + return compareAttributes(this, event.target, 'y'); + }) + .style('fill', (b, i) => `${BAR_COLOR_DEFAULT[i]}`); + actionBarSelection + .filter(function() { + return compareAttributes(this, event.target, 'y'); + }) + .style('opacity', '0'); + }) + .on('mousemove', function(chartData) { + if (chartData.label.length >= CHAR_LIMIT) { + select('.chart-tooltip') + .style('left', `${event.pageX - 300}px`) + .style('top', `${event.pageY - 100}px`) + .text(`${chartData.label}`) + .style('max-width', 'fit-content'); + } else { + select('.chart-tooltip').style('opacity', 0); + } + }); + + // add client count total values to the right + chartSvg + .append('g') + .attr('transform', `translate(${CHART_MARGIN.left}, ${TRANSLATE.down})`) + .selectAll('text') + .data(dataset) + .enter() + .append('text') + .text(d => d.total) + .attr('fill', '#000') + .attr('class', 'total-value') + .style('font-size', '.8rem') + .attr('text-anchor', 'start') + .attr('alignment-baseline', 'middle') + .attr('x', chartData => `${xScale(chartData.total)}%`) + .attr('y', chartData => yScale(chartData.label)); } } diff --git a/ui/app/styles/components/horizontal-bar-charts.scss b/ui/app/styles/components/horizontal-bar-charts.scss deleted file mode 100644 index 0ff97574b84c..000000000000 --- a/ui/app/styles/components/horizontal-bar-charts.scss +++ /dev/null @@ -1,8 +0,0 @@ -// TODO: combine into main chart css file and delete/unimport this file -div.horizontal-bar-chart { - > div.legend { - height: $spacing-l; - margin-top: $spacing-xs; - float: right; - } -} diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index 40f4209dec4c..6ec797e43dda 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -60,7 +60,6 @@ @import './components/features-selection'; @import './components/form-section'; @import './components/global-flash'; -@import './components/horizontal-bar-charts'; @import './components/hover-copy-button'; @import './components/init-illustration'; @import './components/info-table'; diff --git a/ui/app/styles/core/charts.scss b/ui/app/styles/core/charts.scss index 4d9a5de9215d..960170255fb7 100644 --- a/ui/app/styles/core/charts.scss +++ b/ui/app/styles/core/charts.scss @@ -36,12 +36,13 @@ line-height: normal; margin-bottom: $spacing-xs; } - .chart-description { - font-size: $size-8; - font-weight: $font-weight-normal; - color: $ui-gray-700; - margin-bottom: $spacing-xs; - } +} + +p.chart-description { + font-size: $size-8; + font-weight: $font-weight-normal; + color: $ui-gray-700; + margin-bottom: $spacing-xs; } .has-export { @@ -74,27 +75,41 @@ } } -.chart-column-left { +.chart-container-left { grid-column-start: 1; grid-column-end: 4; grid-row-start: 2; grid-row-end: 5; - h2.title { + box-shadow: inset 0 -1px 0 $vault-gray-200; + + h2.chart-title { font-size: $size-5; font-weight: $font-weight-bold; - margin-bottom: $spacing-l; } } -.chart-column-right { +.chart-container-right { grid-column-start: 4; grid-column-end: 8; grid-row-start: 2; grid-row-end: 5; - h2.title { + box-shadow: inset 0 -1px 0 $vault-gray-200; + + h2.chart-title { font-size: $size-5; font-weight: $font-weight-bold; - margin-bottom: $spacing-l; + } +} + +.horizontal-bar-chart { + .tick > text { + font-weight: $font-weight-semibold; + font-size: $size-8; + } + > div.legend { + height: $spacing-l; + margin-top: $spacing-xs; + float: right; } } @@ -141,22 +156,40 @@ } } -.legend-container-center { +.timestamp { + grid-column-start: 1; + grid-column-end: 2; + grid-row-start: 5; + color: $ui-gray-500; + font-size: $size-9; + align-self: end; +} + +.light-dot { + background-color: #bfd4ff; + height: 10px; + width: 10px; + border-radius: 50%; + display: inline-block; + padding-right: 10px; +} + +.dark-dot { + background-color: #1563ff; + height: 10px; + width: 10px; + border-radius: 50%; + display: inline-block; +} + +.legend-container { grid-row-start: 5; grid-column-start: 3; grid-column-end: 4; height: $spacing-l; align-self: center; justify-self: center; -} - -.legend-container-right { - grid-row-start: 5; - grid-column-start: 5; - grid-column-end: 6; - height: $spacing-l; - align-self: start; - justify-self: start; + font-size: $size-9; } .chart-tooltip { diff --git a/ui/app/templates/components/clients/dashboard.hbs b/ui/app/templates/components/clients/dashboard.hbs index 59f9f063e82e..ec5d327747ac 100644 --- a/ui/app/templates/components/clients/dashboard.hbs +++ b/ui/app/templates/components/clients/dashboard.hbs @@ -136,27 +136,29 @@ {{!-- ARG TODO end of part that goes to Running Client --}} {{#if this.showGraphs}} {{!-- ARG TODO chart playground --}} - - + - - Export attribution data - + + Export attribution data + {{!-- ARG TODO this is the search select from the old graph that can be reused --}} {{!--
diff --git a/ui/app/templates/components/clients/horizontal-bar-charts.hbs b/ui/app/templates/components/clients/horizontal-bar-charts.hbs index 2ac8d2894f6a..6898fc7d1b3a 100644 --- a/ui/app/templates/components/clients/horizontal-bar-charts.hbs +++ b/ui/app/templates/components/clients/horizontal-bar-charts.hbs @@ -13,25 +13,32 @@
-
-

New Clients

+
+

New Clients

+

{{@newClientsDescription}}

-
-

Total monthly clients

+
+

Total monthly clients

+

{{@totalDescription}}

-
- LEGEND - {{!-- --}} +
+ Updated Nov 15 2021, 4:07:32 pm +
+ +
+ {{!-- TO DO - fix styling --}} +

{{@chartLegend.0.label}}

+ {{@chartLegend.1.label}}
diff --git a/ui/app/templates/components/clients/total-client-usage.hbs b/ui/app/templates/components/clients/total-client-usage.hbs index 97ed43eacee9..f245f0a609e4 100644 --- a/ui/app/templates/components/clients/total-client-usage.hbs +++ b/ui/app/templates/components/clients/total-client-usage.hbs @@ -23,6 +23,10 @@

{{@dataTwoData}}

+
+ Updated Nov 15 2021, 4:07:32 pm +
+
legend {{!-- --}}