Skip to content

Commit

Permalink
feat(metric): support array of values (#2428)
Browse files Browse the repository at this point in the history
  • Loading branch information
nickofthyme authored May 17, 2024
1 parent cc438a1 commit e448bd7
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 18 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 16 additions & 3 deletions packages/charts/api/charts.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2059,7 +2059,7 @@ export type MetricBase = {
};

// @alpha (undocumented)
export type MetricDatum = MetricWNumber | MetricWText | MetricWProgress | MetricWTrend;
export type MetricDatum = MetricWNumber | MetricWText | MetricWNumberArrayValues | MetricWStringArrayValues | MetricWProgress | MetricWTrend;

// @public
export type MetricElementEvent = {
Expand Down Expand Up @@ -2118,19 +2118,32 @@ export type MetricWNumber = MetricBase & {
targetFormatter?: ValueFormatter;
};

// @alpha (undocumented)
export type MetricWNumberArrayValues = MetricBase & {
value: Array<number>;
valueFormatter: ValueFormatter<number>;
};

// @alpha (undocumented)
export type MetricWProgress = MetricWNumber & {
domainMax: number;
progressBarDirection: LayoutDirection;
};

// @alpha (undocumented)
export type MetricWStringArrayValues = MetricBase & {
value: Array<string>;
valueFormatter?: never;
};

// @alpha (undocumented)
export type MetricWText = MetricBase & {
value: string;
valueFormatter?: never;
};

// @alpha (undocumented)
export type MetricWTrend = (MetricWNumber | MetricWText) & {
export type MetricWTrend = (MetricWNumber | MetricWText | MetricWNumberArrayValues | MetricWStringArrayValues) & {
trend: {
x: number;
y: number;
Expand Down Expand Up @@ -3407,7 +3420,7 @@ export const useTooltipContext: <D extends BaseDatum = any, SI extends SeriesIde
export type ValueAccessor<D extends BaseDatum = Datum> = (d: D) => AdditiveNumber;

// @public (undocumented)
export type ValueFormatter = (value: number) => string;
export type ValueFormatter<T = number> = (value: T) => string;

// Warning: (ae-forgotten-export) The symbol "ValueGetterName" needs to be exported by the entry point index.d.ts
//
Expand Down
43 changes: 34 additions & 9 deletions packages/charts/src/chart_types/metric/renderer/dom/text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@ import { isFiniteNumber, LayoutDirection, renderWithProps } from '../../../../ut
import { Size } from '../../../../utils/dimensions';
import { wrapText } from '../../../../utils/text/wrap';
import { MetricStyle } from '../../../../utils/themes/theme';
import { isMetricWNumber, isMetricWProgress, MetricDatum } from '../../specs';
import { isMetricWNumber, isMetricWNumberArrayValues, isMetricWProgress, MetricDatum } from '../../specs';

type BreakPoint = 's' | 'm' | 'l' | 'xl' | 'xxl' | 'xxxl';

interface TextParts {
emphasis: 'small' | 'normal';
text: string;
}

const WIDTH_BP: [number, number, BreakPoint][] = [
[0, 180, 's'],
[180, 250, 'm'],
Expand Down Expand Up @@ -172,7 +177,7 @@ export const MetricText: React.FunctionComponent<{
progressBarSize: 'small';
locale: string;
}> = ({ id, datum, panel, style, onElementClick, highContrastTextColor, progressBarSize, locale }) => {
const { extra, value, body } = datum;
const { extra, body } = datum;

const size = findRange(WIDTH_BP, panel.width);
const hasProgressBar = isMetricWProgress(datum);
Expand All @@ -188,12 +193,9 @@ export const MetricText: React.FunctionComponent<{
const titleWidthMaxSize = size === 's' ? '100%' : '80%';
const titlesWidth = `min(${titleWidthMaxSize}, calc(${titleWidthMaxSize} - ${datum.icon ? '24px' : '0px'}))`;

const isNumericalMetric = isMetricWNumber(datum);
const textParts = isNumericalMetric
? isFiniteNumber(value)
? splitNumericSuffixPrefix(datum.valueFormatter(value))
: [{ emphasis: 'normal', text: style.nonFiniteText }]
: [{ emphasis: 'normal', text: datum.value }];
const isNumericalMetric = isMetricWNumber(datum) || isMetricWNumberArrayValues(datum);
const textParts = getTextParts(datum, style);

const TitleElement = () => (
<span
style={{
Expand Down Expand Up @@ -305,7 +307,30 @@ export const MetricText: React.FunctionComponent<{
);
};

function splitNumericSuffixPrefix(text: string): { emphasis: 'normal' | 'small'; text: string }[] {
function getTextParts(datum: MetricDatum, style: MetricStyle): TextParts[] {
const values = Array.isArray(datum.value) ? datum.value : [datum.value];
const valueFormatter =
isMetricWNumber(datum) || isMetricWNumberArrayValues(datum) ? datum.valueFormatter : (v: number) => `${v}`;
const textParts = values.reduce<TextParts[]>((acc, value, i, { length }) => {
const parts: TextParts[] =
typeof value === 'number'
? isFiniteNumber(value)
? splitNumericSuffixPrefix(valueFormatter(value))
: [{ emphasis: 'normal', text: style.nonFiniteText }]
: [{ emphasis: 'normal', text: value }];

if (i < length - 1) {
parts.push({ emphasis: 'normal', text: ', ' });
}
return [...acc, ...parts];
}, []);

if (!Array.isArray(datum.value)) return textParts;

return [{ emphasis: 'normal', text: '[' }, ...textParts, { emphasis: 'normal', text: ']' }];
}

function splitNumericSuffixPrefix(text: string): TextParts[] {
return text
.split('')
.reduce<{ emphasis: 'normal' | 'small'; textParts: string[] }[]>((acc, curr) => {
Expand Down
43 changes: 38 additions & 5 deletions packages/charts/src/chart_types/metric/specs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ export type MetricBase = {
/** @alpha */
export type MetricWText = MetricBase & {
value: string;
valueFormatter?: never;
};

/** @alpha */
export type MetricWNumberArrayValues = MetricBase & {
value: Array<number>;
valueFormatter: ValueFormatter<number>;
};

/** @alpha */
export type MetricWStringArrayValues = MetricBase & {
value: Array<string>;
valueFormatter?: never;
};

/** @alpha */
Expand Down Expand Up @@ -78,15 +91,21 @@ export const MetricTrendShape = Object.freeze({
export type MetricTrendShape = $Values<typeof MetricTrendShape>;

/** @alpha */
export type MetricWTrend = (MetricWNumber | MetricWText) & {
export type MetricWTrend = (MetricWNumber | MetricWText | MetricWNumberArrayValues | MetricWStringArrayValues) & {
trend: { x: number; y: number }[];
trendShape: MetricTrendShape;
trendA11yTitle?: string;
trendA11yDescription?: string;
};

/** @alpha */
export type MetricDatum = MetricWNumber | MetricWText | MetricWProgress | MetricWTrend;
export type MetricDatum =
| MetricWNumber
| MetricWText
| MetricWNumberArrayValues
| MetricWStringArrayValues
| MetricWProgress
| MetricWTrend;

/** @alpha */
export interface MetricSpec extends Spec {
Expand Down Expand Up @@ -121,13 +140,24 @@ export function isBulletMetric(datum: MetricDatum): datum is BulletMetricWProgre
return Array.isArray((datum as BulletMetricWProgress).domain);
}

/** @internal */
export function isMetricWNumberArrayValues(datum: MetricDatum): datum is MetricWNumberArrayValues {
return Array.isArray(datum.value) && typeof datum.value[0] === 'number' && datum.hasOwnProperty('valueFormatter');
}

/** @internal */
export function isMetricWStringArrayValues(datum: MetricDatum): datum is MetricWStringArrayValues {
return Array.isArray(datum.value) && typeof datum.value[0] === 'string';
}

/** @internal */
export function isMetricWNumber(datum: MetricDatum): datum is MetricWNumber {
return typeof datum.value === 'number' && datum.hasOwnProperty('valueFormatter');
return 'value' in datum && typeof datum.value === 'number' && datum.hasOwnProperty('valueFormatter');
}

/** @internal */
export function isMetricWText(datum: MetricDatum): datum is MetricWNumber {
return typeof datum.value === 'string';
return 'value' in datum && typeof datum.value === 'string';
}

/** @internal */
Expand All @@ -141,7 +171,10 @@ export function isMetricWProgress(datum: MetricDatum): datum is MetricWProgress
/** @internal */
export function isMetricWTrend(datum: MetricDatum): datum is MetricWTrend {
return (
(isMetricWNumber(datum) || isMetricWText(datum)) &&
(isMetricWNumber(datum) ||
isMetricWText(datum) ||
isMetricWNumberArrayValues(datum) ||
isMetricWStringArrayValues(datum)) &&
datum.hasOwnProperty('trend') &&
!datum.hasOwnProperty('domainMax')
);
Expand Down
2 changes: 2 additions & 0 deletions packages/charts/src/chart_types/specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export {
MetricBase,
MetricWText,
MetricWNumber,
MetricWNumberArrayValues,
MetricWStringArrayValues,
MetricWProgress,
MetricWTrend,
MetricTrendShape,
Expand Down
2 changes: 1 addition & 1 deletion packages/charts/src/utils/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ export function mergePartial<T>(
}

/** @public */
export type ValueFormatter = (value: number) => string;
export type ValueFormatter<T = number> = (value: T) => string;
/** @public */
export type ValueAccessor<D extends BaseDatum = Datum> = (d: D) => AdditiveNumber;
/** @public */
Expand Down
Loading

0 comments on commit e448bd7

Please sign in to comment.