Skip to content

Commit 54ca7cb

Browse files
authored
feat(metric): Add ability to vertically align the metrics values to the middle of the chart (#2783)
The `valuePosition` prop in the MetricStyle theme now supports a new 'middle' value, allowing the primary metric value to be vertically centered within the chart area.
1 parent fbb8834 commit 54ca7cb

11 files changed

Lines changed: 43 additions & 7 deletions

File tree

18.4 KB
Loading

e2e/tests/metric_stories.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ test.describe('Metric', () => {
112112
);
113113
});
114114

115+
test('should render metric with value in middle position', async ({ page }) => {
116+
await common.expectChartAtUrlToMatchScreenshot(page)(
117+
`http://localhost:9001/?path=/story/metric-alpha--basic&globals=toggles.showHeader:true;toggles.showChartTitle:false;toggles.showChartDescription:false;toggles.showChartBoundary:false;theme:light&knob-value text-align=center&knob-value position=middle`,
118+
{
119+
action: async () => await common.setResizeDimensions(page)({ height: 300, width: 200 }),
120+
},
121+
);
122+
});
123+
115124
test('should use restrictive breakpoint at 99px height', async ({ page }) => {
116125
await common.expectChartAtUrlToMatchScreenshot(page)(
117126
`http://localhost:9001/?path=/story/metric-alpha--layout&globals=theme:light;toggles.showHeader:true;toggles.showChartTitle:false;toggles.showChartDescription:false;toggles.showChartBoundary:false&knob-Aria description_Secondary metric=This is a description&knob-Bar background_Colors=rgb(194,201,214)&knob-Blending background_Colors=rgba(255,255,255,1)&knob-Color by value_Secondary metric=true&knob-Custom extra_Secondary metric=last <b>5m</b>&knob-Extra element alignment_Text configuration and position=left&knob-Is numeric metric=true&knob-Label_Secondary metric=Last week&knob-Metric color_Colors=rgb(246,217,143)&knob-Metric icon align_Text configuration and position=right&knob-Primary metric alignment_Text configuration and position=left&knob-Primary metric font size (only if custom font size selected)_Text configuration and position=40&knob-Primary metric font size mode_Text configuration and position=default&knob-Primary metric position_Text configuration and position=top&knob-Progress bar direction=horizontal&knob-Progress max=100&knob-Secondary metric label position_Secondary metric=before&knob-Secondary metric trend icon position_Secondary metric=after&knob-Secondary metric trend icon_Secondary metric=↑&knob-Secondary metric value color_Secondary metric=rgb(93, 191, 149)&knob-Secondary metric value_Secondary metric=87.20&knob-Show extra_Secondary metric=true&knob-Show metric icon_Text configuration and position=true&knob-Title= Count of records Count of records Count of records Count of records&knob-Title and subtitle alignment_Text configuration and position=left&knob-Title weight_Text configuration and position=normal&knob-Trend a11y description=The trend shows a peak of CPU usage in the last 5 minutes&knob-Trend a11y title=The Cluster CPU Usage trend&knob-Trend data points=30&knob-Trend shape=area&knob-Value=5&knob-Value color_Colors=#065B58&knob-Subtitle=Subtitle Subtitle Subtitle Subtitle Subtitle&knob-Visualization type=none&knob-Value prefix=&knob-Value postfix= &knob-Use custom extra_Secondary metric=&knob-Use value color_Colors=&knob-Use blending background_Colors=&knob-Show value icon=`,

packages/charts/api/charts.api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2174,7 +2174,7 @@ export interface MetricStyle {
21742174
// (undocumented)
21752175
valueFontSize: 'default' | 'fit' | number;
21762176
// (undocumented)
2177-
valuePosition: 'top' | 'bottom';
2177+
valuePosition: 'top' | 'middle' | 'bottom';
21782178
// (undocumented)
21792179
valueTextAlign: Extract<TextAlign, 'left' | 'center' | 'right'>;
21802180
}

packages/charts/src/chart_types/metric/renderer/dom/_text.scss

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
}
4141
}
4242

43-
.echMetricText__extraBlock {
43+
&__extraBlock {
4444
display: flex;
4545
align-items: center;
4646
overflow: hidden;
@@ -55,6 +55,11 @@
5555
&--right {
5656
justify-content: flex-end;
5757
}
58+
59+
// Vertical position modifiers
60+
&--middle {
61+
padding-top: 0;
62+
}
5863
}
5964

6065
&__valueBlock {

packages/charts/src/chart_types/metric/renderer/dom/text.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const GRID_SPAN_THREE = '1 / span 3';
2424

2525
const gridRows = {
2626
top: { value: '1', titles: '2', body: '3', extra: '4' },
27+
middle: { value: '3', titles: '1', body: '2', extra: '5' },
2728
bottom: { value: '4', titles: '1', body: '2', extra: '3' },
2829
};
2930

@@ -39,8 +40,14 @@ const gridColumnsValuePositionBottom = {
3940
right: { value: GRID_SPAN_THREE, titles: '1 / span 2', body: GRID_SPAN_THREE, extra: GRID_SPAN_THREE },
4041
};
4142

43+
const gridColumnsValuePositionMiddle = {
44+
left: { value: GRID_SPAN_THREE, titles: '2 / span 2', body: GRID_SPAN_THREE, extra: GRID_SPAN_THREE },
45+
right: { value: GRID_SPAN_THREE, titles: '1 / span 2', body: GRID_SPAN_THREE, extra: GRID_SPAN_THREE },
46+
};
47+
4248
const gridColumns = {
4349
top: gridColumnsValuePostitionTop,
50+
middle: gridColumnsValuePositionMiddle,
4451
bottom: gridColumnsValuePositionBottom,
4552
};
4653

@@ -52,6 +59,7 @@ const getGridTemplateColumnsWithIcon = (iconSize: number) => {
5259

5360
const gridTemplateRows = {
5461
bottom: `min-content auto min-content min-content`,
62+
middle: `min-content 1fr min-content 1fr min-content`,
5563
top: `min-content min-content auto min-content`,
5664
};
5765

@@ -179,7 +187,11 @@ export const MetricText: React.FC<MetricTextprops> = ({
179187

180188
{/* Extra Block */}
181189
<div
182-
className={classNames('echMetricText__extraBlock', `echMetricText__extraBlock--${style.extraTextAlign}`)}
190+
className={classNames(
191+
'echMetricText__extraBlock',
192+
`echMetricText__extraBlock--${style.extraTextAlign}`,
193+
`echMetricText__extraBlock--${valuePosition}`,
194+
)}
183195
style={{
184196
gridRow: currentGridRows.extra,
185197
gridColumn: currentGridColumns.extra,

packages/charts/src/chart_types/metric/renderer/dom/text_measurements.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ export function getMetricTextPartDimensions(
217217
locale,
218218
style.valueFontSize === 'fit',
219219
progressBarHeight,
220+
style.valuePosition === 'middle' ? 0 : ELEMENT_PADDING,
220221
),
221222
textParts: getTextParts(datum, style),
222223
iconGridColumnWidth,
@@ -300,6 +301,7 @@ function computeMetricTextLayout(
300301
locale: string,
301302
fit: boolean,
302303
progressBarHeight: number, // with padding
304+
extraPaddingTop: number,
303305
): MetricTextLayout {
304306
const maxTitlesWidth = 0.95 * panel.width - (datum.icon ? 24 : 0) - 2 * PADDING;
305307

@@ -343,7 +345,7 @@ function computeMetricTextLayout(
343345
const actualTitleHeight = titleLines.length > 0 ? titleLines.length * titleLineHeight : 0;
344346
const actualSubtitleHeight =
345347
subtitleLines.length > 0 ? subtitleLines.length * subtitleLineHeight + ELEMENT_PADDING : 0; // Subtitle padding top 5px
346-
const actualExtraHeight = breakpoints.extra ? extraHeight + ELEMENT_PADDING : 0; // Extra padding top 5px
348+
const actualExtraHeight = breakpoints.extra ? extraHeight + extraPaddingTop : 0;
347349

348350
const nonValueElementsHeight =
349351
actualTitleHeight + actualSubtitleHeight + actualExtraHeight + progressBarHeight + PADDING * 2;

packages/charts/src/utils/themes/theme.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ export interface MetricStyle {
327327
titlesTextAlign: Extract<TextAlign, 'left' | 'center' | 'right'>;
328328
extraTextAlign: Extract<TextAlign, 'left' | 'center' | 'right'>;
329329
valueTextAlign: Extract<TextAlign, 'left' | 'center' | 'right'>;
330-
valuePosition: 'top' | 'bottom';
330+
valuePosition: 'top' | 'middle' | 'bottom';
331331
iconAlign: Extract<HorizontalAlignment, 'left' | 'right'>;
332332

333333
titleWeight: MetricFontWeight;

storybook/stories/metric/1_basic.story.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export const Example: ChartsStory = (_, { title: storyTitle, description }) => {
8181
const titlesTextAlign = getTextAlignKnob('title text-align', 'left');
8282
const valueTextAlign = getTextAlignKnob('value text-align', 'right');
8383
const extraTextAlign = getTextAlignKnob('extra text-align', 'right');
84-
const valuePosition = select('value position', { Bottom: 'bottom', Top: 'top' }, 'bottom');
84+
const valuePosition = select('value position', { Bottom: 'bottom', Middle: 'middle', Top: 'top' }, 'bottom');
8585
const iconAlign = select(
8686
'icon align',
8787
{

storybook/stories/metric/2_grid.story.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export const Example: ChartsStory = (_, { title, description }) => {
6363
'default',
6464
);
6565
const valueFontSize = number('value font size (px)', 40, { min: 0, step: 10 });
66+
const valuePosition = select('value position', { Bottom: 'bottom', Middle: 'middle', Top: 'top' }, 'bottom');
6667

6768
const data: (MetricDatum | undefined)[] = useMemo(
6869
() => [
@@ -227,6 +228,7 @@ export const Example: ChartsStory = (_, { title, description }) => {
227228
metric: {
228229
emptyBackground,
229230
valueFontSize: valueFontSizeMode === 'custom' ? valueFontSize : valueFontSizeMode,
231+
valuePosition,
230232
},
231233
}}
232234
baseTheme={useBaseTheme()}

storybook/stories/metric/3_body.story.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export const Example: ChartsStory = (_, { title: storyTitle, description }) => {
5353
const valueIconType = customKnobs.eui.getIconTypeKnob('EUI value icon glyph name', 'sortUp');
5454
const showBody = boolean('show body contents', true);
5555
const showBodyArea = boolean('show full body area', false);
56+
const valuePosition = select('value position', { Bottom: 'bottom', Middle: 'middle', Top: 'top' }, 'bottom');
5657
const getIcon =
5758
(type: string) =>
5859
({ width, height, color }: { width: number; height: number; color: string }) => (
@@ -112,6 +113,11 @@ export const Example: ChartsStory = (_, { title: storyTitle, description }) => {
112113
return (
113114
<Chart title={storyTitle} description={description}>
114115
<Settings
116+
theme={{
117+
metric: {
118+
valuePosition,
119+
},
120+
}}
115121
baseTheme={useBaseTheme()}
116122
onElementClick={([d]) => {
117123
if (isMetricElementEvent(d)) {

0 commit comments

Comments
 (0)