Skip to content

Commit

Permalink
UI/ Client counts range (running total component) (#13477)
Browse files Browse the repository at this point in the history
* grid for stacked charts

* pass in data as arg from parent

* pull out vertical bar chart component

* refactor to use vertical bar chart component

* remove any chart handling stuff from parent

* rename variables

* refactor horizontal bar chart into separate component

* move descriptions to inside template (not passed in)

* constructs attribution copy

* add sample response to mirage config

* change indenting

* rename to MonthlyUsage

* change name to running totals

* rename variable

* finishes line chart

* pull constants to util

* cleanup add todos

* fix formatNumbers return"

* comments and cleanup

* adds tooltip to line chart

* make cover area larger

* fixes tooltip styling

* adds tooltip styling"

* adds tooltip modal to horizontal chart

* finishes tooltip for horizontal chart

* remove click event arg
  • Loading branch information
hellobontempo authored Jan 3, 2022
1 parent f658c80 commit aeedf70
Show file tree
Hide file tree
Showing 19 changed files with 864 additions and 275 deletions.
45 changes: 45 additions & 0 deletions ui/app/components/clients/attribution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Component from '@glimmer/component';

// TODO: fill out below!!
/**
* @module Attribution
* Attribution components are used to...
*
* @example
* ```js
* <Attribution @requiredParam={requiredParam} @optionalParam={optionalParam} @param1={{param1}}>
* Pass in export button
* </Attribution>
* ```
* @param {object} requiredParam - requiredParam is...
* @param {string} [optionalParam] - optionalParam is...
* @param {string} [param1=defaultValue] - param1 is...
*/

export default class Attribution extends Component {
get dateRange() {
// some conditional that returns "date range" or "month" depending on what the params are
return 'date range';
}

get chartText() {
// something that determines if data is by namespace or by auth method
// and returns text
// if byNamespace
return {
description:
'This data shows the top ten namespaces by client count and can be used to understand where clients are originating. Namespaces are identified by path. To see all namespaces, export this data.',
newCopy: `The new clients in the namespace for this ${this.dateRange}.
This aids in understanding which namespaces create and use new clients
${this.dateRange === 'date range' ? ' over time.' : '.'}`,
totalCopy: `The total clients in the namespace for this ${this.dateRange}. This number is useful for identifying overall usage volume.`,
};
// if byAuthMethod
// return
// byAuthMethod = {
// description: "This data shows the top ten authentication methods by client count within this namespace, and can be used to understand where new clients and total clients are originating. Authentication methods are organized by path.",
// newCopy: `The new clients used by the auth method for this {{@range}}. This aids in understanding which auth methods create and use new clients ${this.dateRange === "date range" ? " over time." : "."}`,
// totalCopy: `The total clients used by the auth method for this ${this.dateRange}. This number is useful for identifying overall usage volume. `
// }
}
}
58 changes: 56 additions & 2 deletions ui/app/components/clients/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { format } from 'date-fns';
export default class Dashboard extends Component {
maxNamespaces = 10;
chartLegend = [
{ key: 'distinct_entities', label: 'Direct entities' },
{ key: 'non_entity_tokens', label: 'Active direct tokens' },
{ key: 'distinct_entities', label: 'unique entities' },
{ key: 'non_entity_tokens', label: 'non-entity tokens' },
];
@tracked selectedNamespace = null;

Expand Down Expand Up @@ -62,6 +62,60 @@ export default class Dashboard extends Component {
});
}

// TODO: dataset for line chart
get lineChartData() {
return [
{ month: '1/21', clients: 100, new: 100 },
{ month: '2/21', clients: 300, new: 200 },
{ month: '3/21', clients: 300, new: 0 },
{ month: '4/21', clients: 300, new: 0 },
{ month: '5/21', clients: 300, new: 0 },
{ month: '6/21', clients: 300, new: 0 },
{ month: '7/21', clients: 300, new: 0 },
{ month: '8/21', clients: 350, new: 50 },
{ month: '9/21', clients: 400, new: 50 },
{ month: '10/21', clients: 450, new: 50 },
{ month: '11/21', clients: 500, new: 50 },
{ month: '12/21', clients: 1000, new: 1000 },
];
}

// TODO: dataset for new monthly clients vertical bar chart (manage in serializer?)
get newMonthlyClients() {
return [
{ month: 'January', distinct_entities: 1000, non_entity_tokens: 322, total: 1322 },
{ month: 'February', distinct_entities: 1500, non_entity_tokens: 122, total: 1622 },
{ month: 'March', distinct_entities: 4300, non_entity_tokens: 700, total: 5000 },
{ month: 'April', distinct_entities: 1550, non_entity_tokens: 229, total: 1779 },
{ month: 'May', distinct_entities: 5560, non_entity_tokens: 124, total: 5684 },
{ month: 'June', distinct_entities: 1570, non_entity_tokens: 142, total: 1712 },
{ month: 'July', distinct_entities: 300, non_entity_tokens: 112, total: 412 },
{ month: 'August', distinct_entities: 1610, non_entity_tokens: 130, total: 1740 },
{ month: 'September', distinct_entities: 1900, non_entity_tokens: 222, total: 2122 },
{ month: 'October', distinct_entities: 500, non_entity_tokens: 166, total: 666 },
{ month: 'November', distinct_entities: 480, non_entity_tokens: 132, total: 612 },
{ month: 'December', distinct_entities: 980, non_entity_tokens: 202, total: 1182 },
];
}

// TODO: dataset for vault usage vertical bar chart (manage in serializer?)
get monthlyUsage() {
return [
{ month: 'January', distinct_entities: 1000, non_entity_tokens: 322, total: 1322 },
{ month: 'February', distinct_entities: 1500, non_entity_tokens: 122, total: 1622 },
{ month: 'March', distinct_entities: 4300, non_entity_tokens: 700, total: 5000 },
{ month: 'April', distinct_entities: 1550, non_entity_tokens: 229, total: 1779 },
{ month: 'May', distinct_entities: 5560, non_entity_tokens: 124, total: 5684 },
{ month: 'June', distinct_entities: 1570, non_entity_tokens: 142, total: 1712 },
{ month: 'July', distinct_entities: 300, non_entity_tokens: 112, total: 412 },
{ month: 'August', distinct_entities: 1610, non_entity_tokens: 130, total: 1740 },
{ month: 'September', distinct_entities: 1900, non_entity_tokens: 222, total: 2122 },
{ month: 'October', distinct_entities: 500, non_entity_tokens: 166, total: 666 },
{ month: 'November', distinct_entities: 480, non_entity_tokens: 132, total: 612 },
{ month: 'December', distinct_entities: 980, non_entity_tokens: 202, total: 1182 },
];
}

// Create namespaces data for csv format
get getCsvData() {
if (!this.args.model.activity || !this.args.model.activity.byNamespace) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { select, event, selectAll } from 'd3-selection';
import { scaleLinear, scaleBand } from 'd3-scale';
import { axisLeft } from 'd3-axis';
import { max, maxIndex } from 'd3-array';
import { BAR_COLOR_HOVER, GREY, LIGHT_AND_DARK_BLUE } from '../../utils/chart-helpers';
import { tracked } from '@glimmer/tracking';

/**
* @module HorizontalBarChart
Expand All @@ -30,74 +32,10 @@ const TRANSLATE = { down: 13 };
const CHAR_LIMIT = 15; // character count limit for y-axis labels to trigger truncating
const LINE_HEIGHT = 24; // each bar w/ padding is 24 pixels thick

// COLOR THEME:
const BAR_COLOR_DEFAULT = ['#BFD4FF', '#1563FF'];
const BAR_COLOR_HOVER = ['#1563FF', '#0F4FD1'];
const BACKGROUND_BAR_COLOR = '#EBEEF2';

const SAMPLE_DATA = [
{
label: 'longlongsuperlongnamespace80/',
non_entity_tokens: 1696,
distinct_entities: 1652,
total: 3348,
},
{
label: 'namespace12/',
non_entity_tokens: 1568,
distinct_entities: 1663,
total: 3231,
},
{
label: 'namespace44/',
non_entity_tokens: 1511,
distinct_entities: 1708,
total: 3219,
},
{
label: 'namespace36/',
non_entity_tokens: 1574,
distinct_entities: 1553,
total: 3127,
},
{
label: 'namespace2/',
non_entity_tokens: 1784,
distinct_entities: 1333,
total: 3117,
},
{
label: 'namespace82/',
non_entity_tokens: 1245,
distinct_entities: 1702,
total: 2947,
},
{
label: 'namespace28/',
non_entity_tokens: 1579,
distinct_entities: 1364,
total: 2943,
},
{
label: 'namespace60/',
non_entity_tokens: 1962,
distinct_entities: 929,
total: 2891,
},
{
label: 'namespace5/',
non_entity_tokens: 1448,
distinct_entities: 1418,
total: 2866,
},
{
label: 'namespace67/',
non_entity_tokens: 1758,
distinct_entities: 1065,
total: 2823,
},
];
export default class HorizontalBarChart extends Component {
@tracked tooltipTarget = '';
@tracked tooltipText = '';

get labelKey() {
return this.args.labelKey || 'label';
}
Expand All @@ -110,6 +48,10 @@ export default class HorizontalBarChart extends Component {
return this.args.dataset[maxIndex(this.args.dataset, d => d.total)];
}

@action removeTooltip() {
this.tooltipTarget = null;
}

@action
renderChart(element, args) {
// chart legend tells stackFunction how to stack/organize data
Expand All @@ -120,7 +62,6 @@ export default class HorizontalBarChart extends Component {
// let dataset = SAMPLE_DATA;
let stackedData = stackFunction(dataset);
let labelKey = this.labelKey;
let handleClick = this.args.onClick;

let xScale = scaleLinear()
.domain([0, max(dataset.map(d => d.total))])
Expand All @@ -144,7 +85,7 @@ export default class HorizontalBarChart extends Component {
.append('g')
// shifts chart to accommodate y-axis legend
.attr('transform', `translate(${CHART_MARGIN.left}, ${CHART_MARGIN.top})`)
.style('fill', (d, i) => BAR_COLOR_DEFAULT[i]);
.style('fill', (d, i) => LIGHT_AND_DARK_BLUE[i]);

let yAxis = axisLeft(yScale).tickSize(0);
yAxis(chartSvg.append('g').attr('transform', `translate(${CHART_MARGIN.left}, ${CHART_MARGIN.top})`));
Expand Down Expand Up @@ -184,7 +125,7 @@ export default class HorizontalBarChart extends Component {
.attr('height', `${LINE_HEIGHT}px`)
.attr('x', '0')
.attr('y', chartData => yScale(chartData[labelKey]))
.style('fill', `${BACKGROUND_BAR_COLOR}`)
.style('fill', `${GREY}`)
.style('opacity', '0')
.style('mix-blend-mode', 'multiply');

Expand All @@ -204,59 +145,45 @@ export default class HorizontalBarChart extends Component {

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}`);
select(elementA).attr(`${attr}`) === select(elementB).attr(`${attr}`);

// MOUSE AND CLICK EVENTS FOR DATA BARS
// MOUSE EVENTS FOR DATA BARS
actionBars
.on('click', function(chartData) {
if (handleClick) {
handleClick(chartData);
}
})
.on('mouseover', function() {
select(this).style('opacity', 1);
.on('mouseover', data => {
let hoveredElement = actionBars.filter(bar => bar.label === data.label).node();
this.tooltipTarget = hoveredElement;
this.tooltipText = `${Math.round((data.total * 100) / 19000)}% of total client counts:
${data.non_entity_tokens} non-entity tokens, ${data.distinct_entities} unique entities.`;

select(hoveredElement).style('opacity', 1);

dataBars
.filter(function() {
return compareAttributes(this, event.target, 'y');
return compareAttributes(this, hoveredElement, '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) / 19000)}% of total client counts:
${chartData.non_entity_tokens} non-entity tokens, ${chartData.distinct_entities} unique entities.
`
);
.style('fill', (b, i) => `${LIGHT_AND_DARK_BLUE[i]}`);
});

// MOUSE EVENTS FOR Y-AXIS LABELS
yLegendBars
.on('click', function(chartData) {
if (handleClick) {
handleClick(chartData);
.on('mouseover', data => {
if (data.label.length >= CHAR_LIMIT) {
let hoveredElement = yLegendBars.filter(bar => bar.label === data.label).node();
this.tooltipTarget = hoveredElement;
this.tooltipText = data.label;
} else {
this.tooltipTarget = null;
}
})
.on('mouseover', function(chartData) {
dataBars
.filter(function() {
return compareAttributes(this, event.target, 'y');
Expand All @@ -267,36 +194,19 @@ export default class HorizontalBarChart extends Component {
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);
this.tooltipTarget = null;
dataBars
.filter(function() {
return compareAttributes(this, event.target, 'y');
})
.style('fill', (b, i) => `${BAR_COLOR_DEFAULT[i]}`);
.style('fill', (b, i) => `${LIGHT_AND_DARK_BLUE[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
Expand Down
Loading

0 comments on commit aeedf70

Please sign in to comment.