Files
redash/viz-lib/src/visualizations/funnel/Renderer/index.tsx
Eric Radman f8934b8312 Switch from numeral to numbro (#6344)
numeraljs is no longer maintained, and incorrectly parses high-precision
floats (such as 1.2e-7) as NaN.
2023-08-15 07:59:18 -04:00

108 lines
3.7 KiB
TypeScript

import { maxBy } from "lodash";
import React, { useMemo } from "react";
import Table from "antd/lib/table";
import Tooltip from "antd/lib/tooltip";
import { RendererPropTypes } from "@/visualizations/prop-types";
import ColorPalette from "@/visualizations/ColorPalette";
import { createNumberFormatter } from "@/lib/value-format";
import prepareData from "./prepareData";
import FunnelBar from "./FunnelBar";
import "./index.less";
function generateRowKeyPrefix() {
return Math.trunc(Math.random() * Number.MAX_SAFE_INTEGER).toString(36) + ":";
}
export default function Renderer({ data, options }: any) {
const funnelData = useMemo(() => prepareData(data.rows, options), [data, options]);
// eslint-disable-next-line react-hooks/exhaustive-deps
const rowKeyPrefix = useMemo(() => generateRowKeyPrefix(), [funnelData]);
const formatValue = useMemo(() => createNumberFormatter(options.numberFormat), [options.numberFormat]);
const formatPercentValue = useMemo(() => {
const format = createNumberFormatter(options.percentFormat);
return (value: any) => {
if (value < options.percentValuesRange.min) {
return `<${format(options.percentValuesRange.min)}`;
}
if (value > options.percentValuesRange.max) {
return `>${format(options.percentValuesRange.max)}`;
}
return format(value);
};
}, [options.percentFormat, options.percentValuesRange]);
const columns = useMemo(() => {
if (funnelData.length === 0) {
return [];
}
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
const maxToPrevious = maxBy(funnelData, d => (isFinite(d.pctPrevious) ? d.pctPrevious : 0)).pctPrevious;
return [
{
title: options.stepCol.displayAs,
dataIndex: "step",
width: "25%",
className: "text-ellipsis",
render: (text: any) => (
<Tooltip title={text} mouseEnterDelay={0} mouseLeaveDelay={0}>
{text}
</Tooltip>
),
},
{
title: options.valueCol.displayAs,
dataIndex: "value",
width: "45%",
align: "center",
render: (value: any, item: any) => (
// @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message
<FunnelBar align="center" color={ColorPalette.Cyan} value={item.pctMax}>
{formatValue(value)}
</FunnelBar>
),
},
{
title: "% Max",
dataIndex: "pctMax",
width: "15%",
align: "center",
render: (value: any) => formatPercentValue(value),
},
{
title: "% Previous",
dataIndex: "pctPrevious",
width: "15%",
align: "center",
render: (value: any) => (
// @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message
<FunnelBar className="funnel-percent-column" value={(value / maxToPrevious)}>
{formatPercentValue(value)}
</FunnelBar>
),
},
];
}, [options.stepCol.displayAs, options.valueCol.displayAs, funnelData, formatValue, formatPercentValue]);
if (funnelData.length === 0) {
return null;
}
return (
<div className="funnel-visualization-container">
<Table
// @ts-expect-error ts-migrate(2322) FIXME: Type '({ title: any; dataIndex: string; width: str... Remove this comment to see the full error message
columns={columns}
dataSource={funnelData}
rowKey={(record, index) => rowKeyPrefix + index}
pagination={false}
/>
</div>
);
}
Renderer.propTypes = RendererPropTypes;