Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Metric): improve default font-sizing #2548

Merged
merged 24 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
nickofthyme marked this conversation as resolved.
Show resolved Hide resolved
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
202 changes: 114 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,13 @@ import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';

import { Metric as MetricComponent } from './metric';
import { getFitValueFontSize, getMetricTextPartDimensions } from './text';
import {
getFittedFontSizes,
getFitValueFontSize,
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 +39,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 +92,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 +102,82 @@ 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' | 'right' }
| { key: string; rowIndex: number; 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' as const,
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 {
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' as const,
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 } =
style.valueFontSize === 'default'
? getSnappedFontSizes(metricsConfigs.fittedValueFontSize, panel.height, style)
: getFittedFontSizes(metricsConfigs.fittedValueFontSize);

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

return (
// eslint-disable-next-line jsx-a11y/no-redundant-roles
Expand All @@ -136,64 +191,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 'type' in config ? (
<li key={config.key} role="presentation">
<div className={config.className} style={{ borderColor: style.border, backgroundColor: emptyBackground }}>
{config.type === 'left' && (
<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