Skip to content

Commit

Permalink
GUI Billing enhancements (issue #3080): custom tooltip for object sto…
Browse files Browse the repository at this point in the history
…rages top 10 chart
  • Loading branch information
AleksandrGorodetskii committed Mar 8, 2023
1 parent b24ecd0 commit f0cec63
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 4 deletions.
14 changes: 12 additions & 2 deletions client/src/components/billing/reports/charts/bar-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
} from './extensions';
import Export from '../export';
import {discounts} from '../discounts';
import {costTickFormatter} from '../utilities';
import {costTickFormatter, renderCustomTooltip} from '../utilities';
import QuotaSummaryChartsTitle from './quota-summary-chart';

function toValueFormat (value) {
Expand Down Expand Up @@ -132,7 +132,8 @@ function BarChart (
quotaGroup,
highlightTickFn,
highlightTickStyle = {},
extraTooltipForItem = ((o) => undefined)
extraTooltipForItem = ((o) => undefined),
renderTooltipFn
}
) {
if (!request) {
Expand Down Expand Up @@ -277,6 +278,15 @@ function BarChart (
// reverse tooltip orders
return b - a;
},
enabled: !renderTooltipFn,
...(renderTooltipFn && {
custom: function (model) {
const item = model.dataPoints && model.dataPoints.length > 0
? filteredData[model.dataPoints[0].index]
: undefined;
renderCustomTooltip(item, model, this._chart, renderTooltipFn);
}
}),
callbacks: {
label: function (tooltipItem, chart) {
const dataset = chart.datasets[tooltipItem.datasetIndex];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,5 +355,8 @@ export function getItemDetailsByMetrics (dataItem, metrics) {
const layoutInfo = (info) =>
// eslint-disable-next-line max-len
`${getStorageClassName(info.tier)}: ${info.total} (current: ${info.current}, old version: ${info.oldVersion})`;
return `\n${infos.map(layoutInfo).join('\n')}`;
return {
text: `\n${infos.map(layoutInfo).join('\n')}`,
infos
};
}
69 changes: 68 additions & 1 deletion client/src/components/billing/reports/storage-report.js
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,73 @@ class StorageReports extends React.Component {
return null;
};

getCustomChartTooltip = (item, model) => {
if (!item || !model) {
return;
}
const {infos} = this.extraTooltipForItemCallback(item.item) || {};
const getMarker = (index) => {
const colors = model.labelColors[index];
const makerStyles = `
background: ${colors.backgroundColor};
border: 1px solid ${colors.borderColor};
width: 13px;
height: 13px;
display: block;
`;
return `<div style="background: currentColor">
<span style="${makerStyles}">
</span>
</div>`;
};
const getMainInfo = () => {
const bodyLines = model.body.map(({lines}) => lines);
return bodyLines.map((line, index) => {
return line && line.length > 0
? `<div style="display: flex; align-items: center">
${getMarker(index)}
<span style="margin-left: 5px">${line}</span>
</div>`
: undefined;
}).filter(Boolean).join('');
};
const getTable = () => {
const rowStyle = `border-bottom: 1px solid ${model.bodyFontColor};`;
const cellStyle = `padding: 0 5px;`;
const rows = infos.map(info => {
return `<tr style="${rowStyle}">
<td style="${cellStyle}">${info.tier}</td>
<td style="${cellStyle}">${info.current}</td>
<td style="${cellStyle}">${info.oldVersion}</td>
<td style="${cellStyle}">${info.total}</td>
</tr>`;
}).join('');
return `
<table style="border-collapse: collapse; margin-top: 10px;">
<tr style="${rowStyle}">
<th style="${cellStyle}">Storage class</th>
<th style="${cellStyle}">Current</th>
<th style="${cellStyle}">Old ver.</th>
<th style="${cellStyle}">Total</th>
</tr>
${rows}
</table>
`;
};
const titleStyles = `
display: inline-block;
margin-bottom: 3px;`;
const title = (model.title || [])
.map(title => `<b style="${titleStyles}">${title}</b>`)
.join('');
const body = `<div style="display: flex; flex-direction: column">
${getMainInfo()}
${getTable()}
</div>
`;
return title.concat(body);
};

render () {
const {
storages,
Expand Down Expand Up @@ -554,7 +621,7 @@ class StorageReports extends React.Component {
highlightTickStyle={{
fontColor: reportThemes.errorColor
}}
extraTooltipForItem={this.extraTooltipForItemCallback}
renderTooltipFn={this.getCustomChartTooltip}
/>
</div>
)
Expand Down
1 change: 1 addition & 0 deletions client/src/components/billing/reports/utilities/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ export {default as numberFormatter} from './number-formatter';
export {getUserDisplayInfo, default as DisplayUser} from './display-user';
export {default as ResizableContainer} from './resizable-container';
export {default as getPeriodMonths} from './get-period-months';
export {default as renderCustomTooltip} from './render-custom-tooltip';
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* Copyright 2017-2023 EPAM Systems, Inc. (https://www.epam.com/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

function renderCustomTooltip (item, model, chart, renderTooltipFn) {
if (!model || !model.body || !renderTooltipFn) {
return;
}
const tooltip = createUpdateTooltip(item, model, chart);
if (!tooltip) {
return;
}
const content = tooltip.querySelector('.chartjs-tooltip-content');
content.innerHTML = `<div class="tooltip-container">
${renderTooltipFn(item, model, chart) || ''}
</div>`;
setTooltipStyles(tooltip, model, chart);
setCaretStyles(tooltip, model);
}

function createUpdateTooltip (item, model, chart) {
let tooltip = document.getElementById('chartjs-tooltip');
if (!tooltip) {
const content = document.createElement('div');
const caret = document.createElement('div');
tooltip = document.createElement('div');
tooltip.id = 'chartjs-tooltip';
content.classList.add('chartjs-tooltip-content');
caret.classList.add('chartjs-tooltip-caret');
tooltip.appendChild(content);
tooltip.appendChild(caret);
chart.canvas.parentNode.appendChild(tooltip);
}
if (model.opacity === 0 || !item) {
tooltip.style.opacity = 0;
return;
}
return tooltip;
};

function setTooltipStyles (tooltip, model, chart) {
const tooltipContainer = tooltip.querySelector('.tooltip-container');
const {xAlign, yAlign, caretY, caretX} = model;
const {
offsetLeft: positionX,
offsetTop: positionY
} = chart.canvas;
const {height, width} = tooltip.getBoundingClientRect();
const space = 10;
let top = positionY + caretY - height;
let left = positionX + caretX - width / 2;
if (yAlign === 'top') {
top += height + space;
} else if (yAlign === 'center') {
top += height / 2;
} else if (yAlign === 'bottom') {
top -= space;
}
if (xAlign === 'left') {
left = left + (width / 2) - model.xPadding - (space / 2);
if (yAlign === 'center') {
left += space * 2;
}
} else if (xAlign === 'right') {
left -= width / 2;
if (yAlign === 'center') {
left -= space;
} else {
left += space;
}
}
tooltip.style.cssText = `
opacity: 1;
position: absolute;
color: ${model.bodyFontColor};
font-family: ${model._bodyFontFamily};
font-style: ${model._bodyFontStyle};
padding: ${model.yPadding}px ${model.xPadding}px;
pointer-events: none;
transition: all 0.3s ease;
background-color: ${model.backgroundColor};
left: ${left}px;
top: ${top}px;
will-change: left, top;
`;
tooltipContainer.style.cssText = `
display: flex;
flex-direction: column;
`;
};

function setCaretStyles (tooltip, model) {
const {xAlign, yAlign} = model;
const {
height: tooltipHeight,
width: tooltipWidth
} = tooltip.getBoundingClientRect();
const caretSize = 7;
const caret = tooltip.querySelector('.chartjs-tooltip-caret');
if (!caret) {
return;
}
caret.style.position = 'absolute';
caret.style.display = 'block';
caret.style.width = '12px';
caret.style.height = '12px';
caret.style.borderColor = 'transparent';
caret.style.borderStyle = 'solid';
caret.style.borderWidth = `${caretSize}px`;
caret.style.top = 'unset';
caret.style.right = 'unset';
caret.style.bottom = 'unset';
caret.style.left = 'unset';
switch (`${yAlign}|${xAlign}`) {
case 'center|left':
caret.style.borderRightColor = model.backgroundColor;
caret.style.top = `${(tooltipHeight / 2) - (caretSize)}px`;
caret.style.left = `-${caretSize * 2}px`;
break;
case 'center|right':
caret.style.borderLeftColor = model.backgroundColor;
caret.style.top = `${(tooltipHeight / 2) - (caretSize)}px`;
caret.style.right = `-${caretSize * 2}px`;
break;
case 'bottom|left':
caret.style.borderTopColor = model.backgroundColor;
caret.style.bottom = `-${caretSize * 2}px`;
caret.style.left = `${caretSize}px`;
break;
case 'bottom|center':
caret.style.borderTopColor = model.backgroundColor;
caret.style.bottom = `-${caretSize * 2}px`;
caret.style.left = `${tooltipWidth / 2 - caretSize}px`;
break;
case 'bottom|right':
caret.style.borderTopColor = model.backgroundColor;
caret.style.bottom = `-${caretSize * 2}px`;
caret.style.left = `${tooltipWidth - caretSize * 2}px`;
break;
}
};

export default renderCustomTooltip;

0 comments on commit f0cec63

Please sign in to comment.