Skip to content

Commit

Permalink
Added KPI visualization
Browse files Browse the repository at this point in the history
  • Loading branch information
daniellangnet committed Oct 24, 2020
1 parent e029783 commit 92efbf4
Show file tree
Hide file tree
Showing 9 changed files with 425 additions and 9 deletions.
69 changes: 60 additions & 9 deletions client/app/components/dashboards/dashboard-grid.less
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,66 @@
overflow: hidden;
}

.counter-visualization-content {
position: absolute;
left: 10px;
top: 15px;
right: 10px;
bottom: 15px;
height: auto;
overflow: hidden;
padding: 0;
.counter-visualization-container {
height: 100%;

.counter-visualization-content {
position: absolute;
left: 10px;
top: 15px;
right: 10px;
bottom: 15px;
height: auto;
overflow: hidden;
padding: 0;
}
}

.kpi-visualization-container {
height: 100%;

.kpi-visualization-content {
position: absolute;
left: 10px;
top: 15px;
right: 10px;
bottom: 15px;
height: auto;
overflow: hidden;
padding: 0;
}
}
}

.query-fixed-layout {
.visualization-renderer > .visualization-renderer-wrapper {
.counter-visualization-container {
// counter is too large on Query pages, so let's add some constraints
max-width: 600px;
max-height: 400px;
// center it
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
}
}

.visualization-renderer > .visualization-renderer-wrapper {
.kpi-visualization-container {
// kpi widget is too large on Query pages, so let's add some constraints
max-width: 600px;
max-height: 400px;
// center it
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
}
}
}

Expand Down
67 changes: 67 additions & 0 deletions viz-lib/src/visualizations/kpi/Editor/FormatSettings.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from "react";
import {Section, Input, InputNumber, Switch, ContextHelp} from "@/components/visualizations/editor";
import { EditorPropTypes } from "@/visualizations";

import { isValueNumber } from "../utils";

export default function FormatSettings({ options, data, onOptionsChange }) {
const inputsEnabled = isValueNumber(data.rows, options);
return (
<React.Fragment>
<Section>
<Input
layout="horizontal"
label={
<React.Fragment>
Values Format
<ContextHelp.NumberFormatSpecs />
</React.Fragment>
}
defaultValue={options.valuesFormat}
onChange={e => onOptionsChange({ valuesFormat: e.target.value })}
/>
</Section>

<Section>
<Input
layout="horizontal"
label={
<React.Fragment>
Percent Format
<ContextHelp.NumberFormatSpecs />
</React.Fragment>
}
defaultValue={options.percentFormat}
onChange={e => onOptionsChange({ percentFormat: e.target.value })}
/>
</Section>

<Section>
<Switch
defaultChecked={options.showDeltaPercentage}
onChange={showDeltaPercentage => onOptionsChange({ showDeltaPercentage })}>
Show delta as percentage
</Switch>
</Section>

<Section>
<Switch
defaultChecked={options.showDeltaAmount}
onChange={showDeltaAmount => onOptionsChange({ showDeltaAmount })}>
Show delta as amount
</Switch>
</Section>

<Section>
<Switch
defaultChecked={options.invertTrendDirection}
onChange={invertTrendDirection => onOptionsChange({ invertTrendDirection })}>
Invert trend direction
</Switch>
</Section>

</React.Fragment>
);
}

FormatSettings.propTypes = EditorPropTypes;
53 changes: 53 additions & 0 deletions viz-lib/src/visualizations/kpi/Editor/GeneralSettings.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { map } from "lodash";
import React from "react";
import { Section, Select, Input, InputNumber, Switch } from "@/components/visualizations/editor";
import { EditorPropTypes } from "@/visualizations";

export default function GeneralSettings({ options, data, visualizationName, onOptionsChange }) {
return (
<React.Fragment>

<Section>
<Select
layout="horizontal"
label="Current Value Column Name"
defaultValue={options.currentValueColName}
onChange={currentValueColName => onOptionsChange({ currentValueColName })}>
{map(data.columns, col => (
<Select.Option key={col.name}>
{col.name}
</Select.Option>
))}
</Select>
</Section>

<Section>
<Select
layout="horizontal"
label="Target Value Column Name"
defaultValue={options.targetValueColName}
onChange={targetValueColName => onOptionsChange({ targetValueColName })}>
<Select.Option value="">No target value</Select.Option>
{map(data.columns, col => (
<Select.Option key={col.name}>
{col.name}
</Select.Option>
))}
</Select>
</Section>

<Section>
<Input
layout="horizontal"
label="Target Value Prefix Label"
placeholder="Current"
defaultValue={options.targetValuePrefixLabel}
onChange={e => onOptionsChange({ targetValuePrefixLabel: e.target.value })}
/>
</Section>

</React.Fragment>
);
}

GeneralSettings.propTypes = EditorPropTypes;
9 changes: 9 additions & 0 deletions viz-lib/src/visualizations/kpi/Editor/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import createTabbedEditor from "@/components/visualizations/editor/createTabbedEditor";

import GeneralSettings from "./GeneralSettings";
import FormatSettings from "./FormatSettings";

export default createTabbedEditor([
{ key: "General", title: "General", component: GeneralSettings },
{ key: "Format", title: "Format", component: FormatSettings },
]);
80 changes: 80 additions & 0 deletions viz-lib/src/visualizations/kpi/Renderer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { isFinite } from "lodash";
import React, { useState, useEffect } from "react";
import cx from "classnames";
import resizeObserver from "@/services/resizeObserver";
import { RendererPropTypes } from "@/visualizations";

import { getKpiData } from "./utils";

import "./render.less";

function getContainerStyles(scale) {
return {
msTransform: `scale(${scale})`,
MozTransform: `scale(${scale})`,
WebkitTransform: `scale(${scale})`,
transform: `scale(${scale})`,
};
}

function getCounterScale(container) {
const inner = container.firstChild;
const scale = Math.min(container.offsetWidth / inner.offsetWidth, container.offsetHeight / inner.offsetHeight);
return Number(isFinite(scale) ? scale : 1).toFixed(2); // keep only two decimal places;
}

export default function Renderer({ data, options, visualizationName }) {
const [scale, setScale] = useState("1.00");
const [container, setContainer] = useState(null);

useEffect(() => {
if (container) {
const unwatch = resizeObserver(container, () => {
setScale(getCounterScale(container));
});
return unwatch;
}
}, [container]);

useEffect(() => {
if (container) {
// update scaling when options or data change (new formatting, values, etc.
// may change inner container dimensions which will not be tracked by `resizeObserver`);
setScale(getCounterScale(container));
}
}, [data, options, container]);

const {
currentValue,
trendDirection,
deltaValue,
targetValuePrefixLabel,
targetValue,
} = getKpiData(data.rows, options);
return (
<div className="kpi-visualization-container">
<div className="kpi-visualization-content" ref={setContainer}>
<div style={getContainerStyles(scale)}>

<div className="kpi-visualization-current-value">
{currentValue}
</div>

{targetValue && (
<div className={cx("kpi-visualization-delta-value", "trend-" + trendDirection)}>
{deltaValue}
</div>
)}

{targetValue && (
<div className="kpi-visualization-target-value">
{targetValuePrefixLabel} ({targetValue})
</div>
)}
</div>
</div>
</div>
);
}

Renderer.propTypes = RendererPropTypes;
20 changes: 20 additions & 0 deletions viz-lib/src/visualizations/kpi/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Renderer from "./Renderer";
import Editor from "./Editor";

const DEFAULT_OPTIONS = {
currentValueColName: "current_value",
targetValueColName: "target_value",
valuesFormat: "0,0[.]00",
percentFormat: "0[.]00%",
};

export default {
type: "KPI",
name: "KPI",
getOptions: options => ({ ...DEFAULT_OPTIONS, ...options }),
Renderer,
Editor,

defaultColumns: 2,
defaultRows: 5,
}
44 changes: 44 additions & 0 deletions viz-lib/src/visualizations/kpi/render.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.kpi-visualization-container {
display: block;
text-align: center;
padding: 15px 10px;
overflow: hidden;
position: relative;

.kpi-visualization-content {
margin: 0;
padding: 0;
font-size: 80px;
line-height: normal;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;

.kpi-visualization-current-value {
font-size: 1em;
display: block;
margin-bottom: 10px;
}

.kpi-visualization-delta-value, .kpi-visualization-target-value {
font-size: .4em;
line-height: 1.3;
}

.kpi-visualization-delta-value {
&.trend-positive {
color: #5cb85c;
}

&.trend-negative {
color: #d9534f;
}
}

.kpi-visualization-target-value {
color: #ccc;
}
}
}
Loading

0 comments on commit 92efbf4

Please sign in to comment.