Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI/tooltip #13397

Merged
merged 11 commits into from
Dec 13, 2021
Merged
136 changes: 110 additions & 26 deletions ui/app/components/clients/total-client-usage.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { select } from 'd3-selection';
import { tracked } from '@glimmer/tracking';
import { max } from 'd3-array';
// eslint-disable-next-line no-unused-vars
import { select, selectAll, node } from 'd3-selection';
import { axisLeft, axisBottom } from 'd3-axis';
import { scaleLinear, scaleBand } from 'd3-scale';
import { format } from 'd3-format';
import { stack } from 'd3-shape';

/**
Expand All @@ -20,54 +25,133 @@ import { stack } from 'd3-shape';

// ARG TODO pull in data
const DATA = [
{ month: 'January', directEntities: 500, nonDirectTokens: 22 },
{ month: 'February', directEntities: 150, nonDirectTokens: 22 },
{ month: 'March', directEntities: 155, nonDirectTokens: 25 },
{ month: 'April', directEntities: 155, nonDirectTokens: 229 },
{ month: 'May', directEntities: 156, nonDirectTokens: 24 },
{ month: 'June', directEntities: 157, nonDirectTokens: 42 },
{ month: 'July', directEntities: 158, nonDirectTokens: 12 },
{ month: 'August', directEntities: 161, nonDirectTokens: 1 },
{ month: 'September', directEntities: 190, nonDirectTokens: 222 },
{ month: 'October', directEntities: 250, nonDirectTokens: 66 },
{ month: 'November', directEntities: 300, nonDirectTokens: 32 },
{ month: 'December', directEntities: 600, nonDirectTokens: 202 },
{ month: 'January', directEntities: 1000, nonEntityTokens: 322, total: 1322 },
{ month: 'February', directEntities: 1500, nonEntityTokens: 122, total: 1622 },
{ month: 'March', directEntities: 700, nonEntityTokens: 125, total: 825 },
{ month: 'April', directEntities: 1550, nonEntityTokens: 229, total: 1779 },
{ month: 'May', directEntities: 1560, nonEntityTokens: 124, total: 1684 },
{ month: 'June', directEntities: 1570, nonEntityTokens: 142, total: 1712 },
{ month: 'July', directEntities: 300, nonEntityTokens: 112, total: 412 },
{ month: 'August', directEntities: 1610, nonEntityTokens: 130, total: 1740 },
{ month: 'September', directEntities: 1900, nonEntityTokens: 222, total: 2122 },
{ month: 'October', directEntities: 500, nonEntityTokens: 166, total: 666 },
{ month: 'November', directEntities: 480, nonEntityTokens: 132, total: 612 },
{ month: 'December', directEntities: 980, nonEntityTokens: 202, total: 1182 },
];

// COLOR THEME:
const BAR_COLOR_DEFAULT = ['#1563FF', '#8AB1FF'];
const BAR_COLOR_DEFAULT = ['#8AB1FF', '#1563FF'];
const BACKGROUND_BAR_COLOR = '#EBEEF2';

const AXES_MARGIN = { xLeft: 10, xDown: 290 }; // makes space for y-axis legend
const TRANSLATE = { none: 0, right: 10, down: -31 };

export default class TotalClientUsage extends Component {
@tracked tooltipTarget = '';
@tracked hoveredLabel = '';
@tracked trackingTest = 0;
@action
registerListener(element) {
let stackFunction = stack().keys(['directEntities', 'nonDirectTokens']);
let stackedData = stackFunction(DATA);
// Define the chart
let dataset = DATA; // will be data passed in as argument

let stackFunction = stack().keys(['directEntities', 'nonEntityTokens']);
let stackedData = stackFunction(dataset);

let yScale = scaleLinear()
.domain([0, 802]) // TODO calculate high of total combined
.range([100, 0]);
.domain([0, max(dataset.map(d => d.total))]) // TODO will need to recalculate when you get the data
.range([0, 80]); // don't want 100% because will cut off

let xScale = scaleBand()
.domain(DATA.map(month => month.month))
.range([0, 100])
.domain(dataset.map(d => d.month))
.range([0, 700]) // set width to fix number of pixels
.paddingInner(0.85);

let chartSvg = select(element);
chartSvg.attr('width', '100%');
chartSvg.attr('height', '100%');

chartSvg.attr('viewBox', `0 5 725 305`); // set aspect ratio

let groups = chartSvg
.selectAll('g')
.data(stackedData)
.enter()
.append('g')
.attr('transform', `translate(${TRANSLATE.right}, ${TRANSLATE.down})`)
.style('fill', (d, i) => BAR_COLOR_DEFAULT[i]);

groups
.selectAll('rect')
.data(stackedData => stackedData)
.enter()
.append('rect')
.attr('width', `${xScale.bandwidth()}%`)
.attr('height', data => `${100 - yScale(data[1])}%`)
.attr('x', data => `${xScale(data.data.month)}%`)
.attr('y', data => `${yScale(data[0]) + yScale(data[1]) - 100}%`);
.attr('width', '7px')
.attr('height', stackedData => `${yScale(stackedData[1] - stackedData[0])}%`)
.attr('x', ({ data }) => xScale(data.month)) // uses destructuring because was data.data.month
.attr('y', data => `${100 - yScale(data[1])}%`); // subtract higher than 100% to give space for x axis ticks

// MAKE AXES //
let svgChartSize = chartSvg.node().getBBox(); // getBBox will return the width and height of svg element
let yAxisScale = scaleLinear()
.domain([0, max(dataset.map(d => d.total))]) // TODO will need to recalculate when you get the data
.range([svgChartSize.height + -TRANSLATE.down, 0]);

// Reference for tickFormat https://www.youtube.com/watch?v=c3MCROTNN8g
let formatNumbers = number => format('.2s')(number).replace('G', 'B'); // for billions to replace G with B.

// customize y-axis
let yAxis = axisLeft(yAxisScale)
.ticks(6)
.tickPadding(10)
.tickSizeInner(-svgChartSize.width) // makes grid lines correct length
.tickFormat(formatNumbers);

yAxis(
chartSvg
.append('g')
.attr('transform', `translate(${TRANSLATE.right})`)
.attr('class', 'axes-lines')
);

// customize x-axis
let xAxisGenerator = axisBottom(xScale);
let xAxis = chartSvg.append('g').call(xAxisGenerator);

xAxis
.attr('transform', `translate(${TRANSLATE.right}, ${AXES_MARGIN.xDown})`)
.attr('class', 'axes-lines');

chartSvg.selectAll('.domain').remove(); // remove domain lines

// creating wider area for tooltip hover
let greyBars = chartSvg.append('g').attr('transform', `translate(${TRANSLATE.none}, ${TRANSLATE.down})`);

let tooltipRect = greyBars
.selectAll('.tooltip-rect')
.data(dataset)
.enter()
.append('rect')
.style('cursor', 'pointer')
.attr('class', 'tooltip-rect')
.attr('height', '100%')
.attr('width', '30px') // three times width
.attr('y', '0') // start at bottom
.attr('x', data => xScale(data.month)) // not data.data because this is not stacked data
.style('fill', `${BACKGROUND_BAR_COLOR}`)
.style('opacity', '0')
.style('mix-blend-mode', 'multiply');

// for tooltip
tooltipRect.on('mouseover', data => {
this.hoveredLabel = data.month;
let node = chartSvg
.selectAll('rect.tooltip-rect')
.filter(data => data.month === this.hoveredLabel)
.node();
this.tooltipTarget = node; // grab the second node from the list of 24 rects
});
}

@action removeTooltip() {
this.tooltipTarget = null;
}
}
82 changes: 62 additions & 20 deletions ui/app/styles/core/charts.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,23 @@
height: 547px; // TODO amend for specific chart heights
width: 100%;

display: grid;
grid-template-columns: repeat(6, 1fr);
grid-template-rows: repeat(5, 1fr);

// ARG TODO we can manage this using css grid
// > div.is-border {
// border: 0.3px solid $ui-gray-200;
// margin-bottom: $spacing-xxs;
// }
}
.single-chart {
display: grid;
grid-template-columns: 1fr 0.3fr 1fr 1fr 1fr 1fr;
grid-template-rows: repeat(5, 1fr);
}

.double-chart {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-template-rows: repeat(5, 1fr);
}

.chart-header {
grid-column-start: 1;
Expand Down Expand Up @@ -53,6 +60,27 @@
}
}

.chart-container {
grid-column-start: 3;
grid-column-end: span col6-end;
grid-row-start: 2;
grid-row-end: span 3;

padding: $spacing-m 0;

svg.chart {
width: 100%;
height: 100%;
}
}

.chart {
.tick > text {
font-weight: $font-weight-semibold;
font-size: $size-8;
}
}

.chart-column-left {
grid-column-start: 1;
grid-column-end: 4;
Expand Down Expand Up @@ -120,22 +148,6 @@
}
}

.chart-container {
grid-column-start: 3;
grid-column-end: span col6-end;
grid-row-start: 2;
grid-row-end: span 3;

padding: 0 0 0 80px;
}

.chart {
.tick > text {
font-weight: $font-weight-semibold;
font-size: $size-8;
}
}

.legend-container-center {
grid-row-start: 5;
grid-column-start: 3;
Expand All @@ -153,3 +165,33 @@
align-self: start;
justify-self: start;
}

.chart-tooltip {
background-color: $ui-gray-700;
color: white;
font-size: 0.929rem;
padding: 10px;
border-radius: 4px;
}

.chart-tooltip-arrow {
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 9px solid $ui-gray-700;
position: absolute;
opacity: 0.8;
bottom: -9px;
left: calc(50% - 5px);
}

.axes-lines {
g > text {
color: $ui-gray-500;
}

g > line {
color: $ui-gray-300;
}
}
21 changes: 11 additions & 10 deletions ui/app/templates/components/clients/dashboard.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -136,16 +136,7 @@
{{!-- ARG TODO end of part that goes to Running Client --}}
{{#if this.showGraphs}}
{{!-- ARG TODO chart playground --}}
<Clients::HorizontalBarCharts
@title="Attribution"
@description="This data can be used to understand where unique clients are originating. These are total unique clients for the time period selected above. In each case, we will show the top ten."
@subText="This data shows the top ten namespaces by client count. They are identified by their path. To see all namespaces, export this data."
@dataset={{this.barChartDataset}}
@chartLegend={{this.chartLegend}}
>
Export attribution data
</Clients::HorizontalBarCharts>


<Clients::TotalClientUsage
@title="Vault usage"
@description="This data can be used to understand how many total clients are using Vault each month for the time period selected above."
Expand All @@ -156,6 +147,16 @@
@dataTwo="Average new clients per month"
@dataTwoData="4"
/>

<Clients::HorizontalBarCharts
@title="Attribution"
@description="This data can be used to understand where unique clients are originating. These are total unique clients for the time period selected above. In each case, we will show the top ten."
@subText="This data shows the top ten namespaces by client count. They are identified by their path. To see all namespaces, export this data."
@dataset={{this.barChartDataset}}
@chartLegend={{this.chartLegend}}
>
Export attribution data
</Clients::HorizontalBarCharts>
{{!-- ARG TODO this is the search select from the old graph that can be reused --}}
{{!-- <div class="column">
<div class="card">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="chart-wrapper">
<div class="chart-wrapper double-chart">
<div class="chart-header has-export">
<div class="header-left">
<h2 class="chart-title">{{@title}}</h2>
Expand Down
24 changes: 20 additions & 4 deletions ui/app/templates/components/clients/total-client-usage.hbs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<div class="chart-wrapper">
<div class="chart-wrapper single-chart">
<div class="chart-header">
<h2 class="chart-title">{{@title}}</h2>
{{#if @description}}
<p class="chart-description">{{@description}}</p>
{{/if}}
</div>
<div class="chart-container">
<svg
<div class="chart-container" {{on "mouseleave" (fn this.removeTooltip)}}>
<svg class="chart"
{{did-insert this.registerListener}}
class="chart"
></svg>
</div>

Expand All @@ -28,4 +27,21 @@
legend
{{!-- <svg class="legend"></svg> --}}
</div>

{{!-- TOOLTIP --}}

{{#if this.tooltipTarget}}
{{!-- Required to set tag name = div https://github.com/yapplabs/ember-modal-dialog/issues/290 --}}
{{!-- Component must be in curly bracket notation --}}
{{#modal-dialog
tagName='div'
tetherTarget=this.tooltipTarget
targetAttachment='bottom middle'
attachment='bottom middle'
offset='100px 0'
}}
<p class="chart-tooltip">{{this.hoveredLabel}}</p>
<div class="chart-tooltip-arrow" />
{{/modal-dialog}}
{{/if}}
</div>
3 changes: 3 additions & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
"ember-load-initializers": "^2.1.2",
"ember-maybe-import-regenerator": "^0.1.6",
"ember-maybe-in-element": "^0.4.0",
"ember-modal-dialog": "^3.0.3",
"ember-power-select-with-create": "^0.6.2",
"ember-promise-helpers": "^1.0.9",
"ember-qunit": "^4.6.0",
Expand All @@ -123,6 +124,7 @@
"ember-svg-jar": "^2.1.0",
"ember-template-lint": "^2.14.0",
"ember-test-selectors": "^2.1.0",
"ember-tether": "^2.0.1",
"ember-truth-helpers": "^2.1.0",
"ember-wormhole": "^0.5.5",
"escape-string-regexp": "^2.0.0",
Expand Down Expand Up @@ -211,6 +213,7 @@
]
},
"dependencies": {
"d3-format": "^3.1.0",
"faker": "^5.5.3",
"handlebars": "^4.3.0",
"highlight.js": "^10.4.1",
Expand Down
Loading