Skip to content

Commit

Permalink
fix(Metric): improve default font-sizing (#2548)
Browse files Browse the repository at this point in the history
  • Loading branch information
markov00 authored Oct 23, 2024
1 parent aa5749d commit 2e1178d
Show file tree
Hide file tree
Showing 54 changed files with 570 additions and 426 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions e2e/tests/metric_stories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,31 @@ test.describe('Metric', () => {
);
});

test('small size with fixed font size', async ({ page }) => {
await common.expectChartAtUrlToMatchScreenshot(page)(
`http://localhost:9001/?path=/story/metric-alpha--basic&knob-value%20font%20mode=custom&knob-value%20font%20size%20(px)=100`,
{
action: async () => await common.setResizeDimensions(page)({ height: 180, width: 180 }),
},
);
});
test('small size with fit font size', async ({ page }) => {
await common.expectChartAtUrlToMatchScreenshot(page)(
`http://localhost:9001/?path=/story/metric-alpha--basic&knob-value%20font%20mode=fit&knob-value%20font%20size%20(px)=100`,
{
action: async () => await common.setResizeDimensions(page)({ height: 180, width: 180 }),
},
);
});
test('small size with default font size', async ({ page }) => {
await common.expectChartAtUrlToMatchScreenshot(page)(
`http://localhost:9001/?path=/story/metric-alpha--basic&knob-value%20font%20mode=default&knob-value%20font%20size%20(px)=100`,
{
action: async () => await common.setResizeDimensions(page)({ height: 180, width: 180 }),
},
);
});

pwEach.describe(['trend', 'bar', 'none'])(
(v) => `Metric - ${v} type`,
(type) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ import { Point } from '../../../../utils/point';
import { LIGHT_THEME } from '../../../../utils/themes/light_theme';
import { MetricStyle } from '../../../../utils/themes/theme';
import { Metric } from '../../../metric/renderer/dom/metric';
import { BulletMetricWProgress, MetricDatum } from '../../../metric/specs';
import { getMetricTextPartDimensions, getSnappedFontSizes } from '../../../metric/renderer/dom/text_measurements';
import { BulletMetricWProgress } from '../../../metric/specs';
import { ActiveValue, getActiveValues } from '../../selectors/get_active_values';
import { getBulletSpec } from '../../selectors/get_bullet_spec';
import { getChartSize } from '../../selectors/get_chart_size';
Expand Down Expand Up @@ -190,30 +191,40 @@ class Component extends React.Component<Props> {
) : undefined,
};

const bulletToMetricStyle = mergePartial(metricStyle, {
barBackground: colorScale(datum.value).hex(),
emptyBackground: Colors.Transparent.keyword,
border: 'gray',
minHeight: 0,
textLightColor: 'white',
textDarkColor: 'black',
nonFiniteText: 'N/A',
valueFontSize: 'default',
});
const panel = { width: size.width / stats.columns, height: size.height / stats.rows };

const textDimensions = getMetricTextPartDimensions(bulletDatum, panel, bulletToMetricStyle, locale);
const sizes = getSnappedFontSizes(
textDimensions.heightBasedSizes.valueFontSize,
panel.height,
bulletToMetricStyle,
);
textDimensions.heightBasedSizes.valueFontSize = sizes.valueFontSize;
textDimensions.heightBasedSizes.valuePartFontSize = sizes.valuePartFontSize;

return (
<Metric
chartId="XX"
datum={bulletDatum as MetricDatum} // forcing internal type use
chartId={`${this.props.chartId}-${stats.rowIndex}-${stats.columnIndex}`}
datum={bulletDatum}
hasTitles={this.props.hasTitles}
totalRows={stats.rows}
totalColumns={stats.columns}
columnIndex={stats.columnIndex}
rowIndex={stats.rowIndex}
style={mergePartial(metricStyle, {
barBackground: colorScale(datum.value).hex(),
emptyBackground: Colors.Transparent.keyword,
border: 'gray',
minHeight: 0,
textLightColor: 'white',
textDarkColor: 'black',
nonFiniteText: 'N/A',
valueFontSize: 'default', // bullet does not support fit mode
})}
locale={locale}
style={bulletToMetricStyle}
backgroundColor={backgroundColor}
contrastOptions={contrastOptions}
panel={{ width: size.width / stats.columns, height: size.height / stats.rows }}
fittedValueFontSize={NaN}
textDimensions={textDimensions}
/>
);
}}
Expand Down
213 changes: 125 additions & 88 deletions packages/charts/src/chart_types/metric/renderer/dom/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';

import { Metric as MetricComponent } from './metric';
import { getFitValueFontSize, getMetricTextPartDimensions } from './text';
import {
getFittedFontSizes,
getFitValueFontSize,
getFixedFontSizes,
getMetricTextPartDimensions,
getSnappedFontSizes,
MetricTextDimensions,
} from './text_measurements';
import { ColorContrastOptions, combineColors, highContrastColor } from '../../../../common/color_calcs';
import { colorToRgba, RGBATupleToString } from '../../../../common/color_library_wrappers';
import { Color } from '../../../../common/colors';
Expand All @@ -33,7 +40,7 @@ import { getResolvedBackgroundColorSelector } from '../../../../state/selectors/
import { getSettingsSpecSelector } from '../../../../state/selectors/get_settings_spec';
import { LIGHT_THEME } from '../../../../utils/themes/light_theme';
import { MetricStyle } from '../../../../utils/themes/theme';
import { MetricSpec } from '../../specs';
import { MetricDatum, MetricSpec } from '../../specs';
import { chartSize } from '../../state/selectors/chart_size';
import { getMetricSpecs } from '../../state/selectors/data';
import { hasChartTitles } from '../../state/selectors/has_chart_titles';
Expand Down Expand Up @@ -86,9 +93,7 @@ function Component({
const { data } = spec;

const totalRows = data.length;
const maxColumns = data.reduce((acc, row) => {
return Math.max(acc, row.length);
}, 0);
const maxColumns = data.reduce((acc, row) => Math.max(acc, row.length), 0);

const panel = { width: width / maxColumns, height: height / totalRows };
const contrastOptions: ColorContrastOptions = {
Expand All @@ -98,31 +103,92 @@ function Component({

const emptyBackgroundRGBA = combineColors(colorToRgba(style.emptyBackground), colorToRgba(backgroundColor));
const emptyBackground = RGBATupleToString(emptyBackgroundRGBA);
const { color: emptyForegroundColor } = highContrastColor(emptyBackgroundRGBA, undefined, contrastOptions);

const fittedValueFontSize =
style.valueFontSize !== 'fit'
? NaN
: data
.flat()
.filter((d) => d !== undefined)
.reduce((acc, datum) => {
const { sizes, progressBarWidth, visibility, textParts } = getMetricTextPartDimensions(
datum,
panel,
style,
locale,
);
const fontSize = getFitValueFontSize(
sizes.valueFontSize,
panel.width - progressBarWidth,
visibility.gapHeight,
textParts,
style.minValueFontSize,
datum.valueIcon !== undefined,
);
return Math.min(acc, fontSize);
}, Number.MAX_SAFE_INTEGER);
const emptyForegroundColor = highContrastColor(emptyBackgroundRGBA, undefined, contrastOptions).color;

const metricsConfigs = data.reduce<{
fittedValueFontSize: number;
configs: Array<
| { key: string; className: string; type: 'left-empty' | 'right-empty' }
| {
key: string;
rowIndex: number;
type: 'metric';
columnIndex: number;
textDimensions: MetricTextDimensions;
datum: MetricDatum;
}
>;
}>(
(acc, columns, rowIndex) => {
acc.configs = acc.configs.concat(
columns.map((datum, columnIndex) => {
const key = `${columnIndex}-${rowIndex}`;
if (!datum) {
// fill with empty panels at the beginning of the row
return {
key,
type: 'left-empty',
className: classNames('echMetric', {
'echMetric--rightBorder': columnIndex < maxColumns - 1,
'echMetric--bottomBorder': rowIndex < totalRows - 1,
'echMetric--topBorder': hasTitles && rowIndex === 0,
}),
};
}
const textDimensions = getMetricTextPartDimensions(datum, panel, style, locale);

const fontSize = getFitValueFontSize(
textDimensions.heightBasedSizes.valueFontSize,
panel.width - textDimensions.progressBarWidth,
textDimensions.visibility.gapHeight,
textDimensions.textParts,
style.minValueFontSize,
datum.valueIcon !== undefined,
);
acc.fittedValueFontSize = Math.min(acc.fittedValueFontSize, fontSize);

return {
type: 'metric',
key,
datum,
columnIndex,
rowIndex,
textDimensions,
};
}),
// adding all missing panels to fill up the row
Array.from({ length: maxColumns - columns.length }, (_, zeroBasedColumnIndex) => {
const columnIndex = zeroBasedColumnIndex + columns.length;
return {
key: `missing-${columnIndex}-${rowIndex}`,
type: 'right-empty',
className: classNames('echMetric', {
'echMetric--bottomBorder': rowIndex < totalRows - 1,
'echMetric--topBorder': hasTitles && rowIndex === 0,
}),
};
}),
);

return acc;
},
{ configs: [], fittedValueFontSize: Number.MAX_SAFE_INTEGER },
);

// update the configs with the globally aligned valueFontSize
const { valueFontSize, valuePartFontSize } =
typeof style.valueFontSize === 'number'
? getFixedFontSizes(style.valueFontSize)
: style.valueFontSize === 'default'
? getSnappedFontSizes(metricsConfigs.fittedValueFontSize, panel.height, style)
: getFittedFontSizes(metricsConfigs.fittedValueFontSize);

metricsConfigs.configs.forEach((config) => {
if (config.type === 'metric') {
config.textDimensions.heightBasedSizes.valueFontSize = valueFontSize;
config.textDimensions.heightBasedSizes.valuePartFontSize = valuePartFontSize;
}
});

return (
// eslint-disable-next-line jsx-a11y/no-redundant-roles
Expand All @@ -136,64 +202,35 @@ function Component({
gridTemplateRows: `repeat(${totalRows}, minmax(${style.minHeight}px, 1fr)`,
}}
>
{data.flatMap((columns, rowIndex) => {
return [
...columns.map((datum, columnIndex) => {
// fill undefined with empty panels
const emptyMetricClassName = classNames('echMetric', {
'echMetric--rightBorder': columnIndex < maxColumns - 1,
'echMetric--bottomBorder': rowIndex < totalRows - 1,
'echMetric--topBorder': hasTitles && rowIndex === 0,
});
return !datum ? (
<li key={`${columnIndex}-${rowIndex}`} role="presentation">
<div
className={emptyMetricClassName}
style={{ borderColor: style.border, backgroundColor: emptyBackground }}
>
<div className="echMetricEmpty" style={{ borderColor: emptyForegroundColor.keyword }}></div>
</div>
</li>
) : (
<li key={`${columnIndex}-${rowIndex}`}>
<MetricComponent
chartId={chartId}
hasTitles={hasTitles}
datum={datum}
totalRows={totalRows}
totalColumns={maxColumns}
rowIndex={rowIndex}
columnIndex={columnIndex}
panel={panel}
style={style}
backgroundColor={backgroundColor}
contrastOptions={contrastOptions}
onElementClick={onElementClick}
onElementOut={onElementOut}
onElementOver={onElementOver}
locale={locale}
fittedValueFontSize={fittedValueFontSize}
/>
</li>
);
}),
// fill the grid row with empty panels
...Array.from({ length: maxColumns - columns.length }, (_, zeroBasedColumnIndex) => {
const columnIndex = zeroBasedColumnIndex + columns.length;
const emptyMetricClassName = classNames('echMetric', {
'echMetric--bottomBorder': rowIndex < totalRows - 1,
'echMetric--topBorder': hasTitles && rowIndex === 0,
});
return (
<li key={`missing-${columnIndex}-${rowIndex}`} role="presentation">
<div
className={emptyMetricClassName}
style={{ borderColor: style.border, backgroundColor: emptyBackground }}
></div>
</li>
);
}),
];
{metricsConfigs.configs.map((config) => {
return config.type !== 'metric' ? (
<li key={config.key} role="presentation">
<div className={config.className} style={{ borderColor: style.border, backgroundColor: emptyBackground }}>
{config.type === 'left-empty' && (
<div className="echMetricEmpty" style={{ borderColor: emptyForegroundColor.keyword }}></div>
)}
</div>
</li>
) : (
<li key={config.key}>
<MetricComponent
chartId={chartId}
hasTitles={hasTitles}
datum={config.datum}
totalRows={totalRows}
totalColumns={maxColumns}
rowIndex={config.rowIndex}
columnIndex={config.columnIndex}
style={style}
backgroundColor={backgroundColor}
contrastOptions={contrastOptions}
onElementClick={onElementClick}
onElementOut={onElementOut}
onElementOver={onElementOver}
textDimensions={config.textDimensions}
/>
</li>
);
})}
</ul>
);
Expand Down
Loading

0 comments on commit 2e1178d

Please sign in to comment.