Skip to content

Commit 476de2b

Browse files
biamalveiropaulinashakirova
authored andcommitted
[Lens][Metric] Fix progress bars to show correct default color (elastic#270282)
## Summary Fixes Lens Metric progress bars staying uncolored when setting a maximum value (regression from elastic#265772). <img width="962" height="528" alt="Screenshot 2026-05-21 at 09 14 21" src="https://github.com/user-attachments/assets/af7104ff-a08d-454e-b64e-748eee2c2b23" /> We fix the too strict condition on `tileColor` that needed an `applyColorTo` property to be defined. This was not the base for progress bars that still use `tileColor` to color the bar. We move the `applyColorTo` check to only be done when in Metric with number, without trend line and without progress bar. **Before:** https://github.com/user-attachments/assets/77abb14b-6ed0-4bee-aa95-4662edb2a215 **After:** https://github.com/user-attachments/assets/9d201882-b867-4a55-bdf4-624caa908199 #### Testing [metric-test.ndjson.zip](https://github.com/user-attachments/files/28084971/metric-test.ndjson.zip) ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) - [x] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels.
1 parent 3b29644 commit 476de2b

4 files changed

Lines changed: 416 additions & 14 deletions

File tree

src/platform/plugins/shared/chart_expressions/expression_metric/public/components/metric_vis.tsx

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,9 @@ export const MetricVis = ({
181181
: primaryMetricColumn.name;
182182
const subtitle = breakdownByColumn ? primaryMetricColumn.name : config.metric.subtitle;
183183

184-
let tileColor = defaultColor;
185-
186-
if (config.metric.applyColorTo) {
187-
if (config.metric.palette?.params && typeof value === 'number') {
188-
tileColor =
189-
getColor(
184+
const paletteColor =
185+
config.metric.palette?.params && typeof value === 'number'
186+
? getColor(
190187
value,
191188
config.metric.palette,
192189
{
@@ -196,11 +193,10 @@ export const MetricVis = ({
196193
},
197194
data,
198195
rowIdx
199-
) ?? defaultColor;
200-
} else {
201-
tileColor = config.metric.color ?? defaultColor;
202-
}
203-
}
196+
) ?? defaultColor
197+
: undefined;
198+
199+
const tileColor = paletteColor ?? config.metric.color ?? defaultColor;
204200

205201
let secondaryMetricProps: SecondaryMetricProps | undefined;
206202
const { secondaryMetric } = config.dimensions;
@@ -288,9 +284,11 @@ export const MetricVis = ({
288284
return {
289285
...baseMetric,
290286
// Override the background and main value color when the color is applied to the value
291-
...(config.metric.applyColorTo === 'value'
292-
? { color: defaultColor, valueColor: tileColor }
293-
: { color: tileColor, valueColor: undefined }),
287+
...(config.metric.applyColorTo === 'value' && { color: defaultColor, valueColor: tileColor }),
288+
...(config.metric.applyColorTo === 'background' && {
289+
color: tileColor,
290+
valueColor: undefined,
291+
}),
294292
};
295293
});
296294

x-pack/platform/plugins/shared/lens/public/visualizations/metric/visualization.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import type {
1212
ExpressionAstExpression,
1313
ExpressionAstFunction,
1414
} from '@kbn/expressions-plugin/common';
15+
// Static EUI token values for assertions
16+
// eslint-disable-next-line @elastic/eui/no-restricted-eui-imports
1517
import { euiThemeVars } from '@kbn/ui-theme';
1618
import { LayerTypes } from '@kbn/expression-xy-plugin/public';
1719
import type { FrameMock } from '../../mocks';
@@ -1055,6 +1057,7 @@ describe('metric visualization', () => {
10551057
visualization.toExpression(
10561058
{
10571059
...fullState,
1060+
applyColorTo: undefined,
10581061
showBar: true,
10591062
color: undefined,
10601063
},
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { spaceTest, tags } from '@kbn/scout';
9+
import { expect } from '@kbn/scout/ui';
10+
import type { KbnClient } from '@kbn/scout';
11+
import { testData } from '../fixtures';
12+
13+
const DASHBOARD_API_PATH = '/api/dashboards';
14+
const DASHBOARD_API_VERSION = '2023-10-31';
15+
16+
const LOGSTASH_TIME_RANGE = {
17+
from: '2015-09-19T06:31:44.000Z',
18+
to: '2015-09-23T18:31:44.000Z',
19+
};
20+
21+
function withSpace(path: string, spaceId: string): string {
22+
return `/s/${spaceId}${path}`;
23+
}
24+
25+
async function createDashboard(client: KbnClient, body: unknown, spaceId: string): Promise<string> {
26+
const response = await client.request<unknown>({
27+
method: 'POST',
28+
path: withSpace(DASHBOARD_API_PATH, spaceId),
29+
body,
30+
headers: { 'elastic-api-version': DASHBOARD_API_VERSION },
31+
});
32+
33+
if (response.status !== 201) {
34+
throw new Error(
35+
`Expected dashboard create status 201, got ${response.status}: ${JSON.stringify(
36+
response.data
37+
)}`
38+
);
39+
}
40+
41+
const { id } = response.data as Record<string, unknown>;
42+
if (typeof id !== 'string' || id.length === 0) {
43+
throw new Error('Dashboard create response: expected a non-empty string id');
44+
}
45+
return id;
46+
}
47+
48+
spaceTest.describe(
49+
'Lens metric progress bar on dashboard (DSL)',
50+
{ tag: tags.stateful.classic },
51+
() => {
52+
let storedDataViewId: string | undefined;
53+
54+
spaceTest.beforeAll(async ({ scoutSpace, apiServices }) => {
55+
await scoutSpace.uiSettings.set({
56+
defaultIndex: testData.DATA_VIEW_ID.LOGSTASH,
57+
'dateFormat:tz': 'UTC',
58+
'timepicker:timeDefaults': JSON.stringify({
59+
from: testData.LOGSTASH_IN_RANGE_DATES.from,
60+
to: testData.LOGSTASH_IN_RANGE_DATES.to,
61+
}),
62+
});
63+
64+
const { data: dataView } = await apiServices.dataViews.create({
65+
title: testData.DATA_VIEW_ID.LOGSTASH,
66+
name: `scout-metric-progress-bar-dv-${Date.now()}`,
67+
timeFieldName: '@timestamp',
68+
spaceId: scoutSpace.id,
69+
});
70+
storedDataViewId = dataView.id;
71+
});
72+
73+
spaceTest.afterAll(async ({ scoutSpace, apiServices }) => {
74+
if (storedDataViewId) {
75+
await apiServices.dataViews.delete(storedDataViewId, scoutSpace.id);
76+
}
77+
await scoutSpace.uiSettings.unset('defaultIndex', 'dateFormat:tz', 'timepicker:timeDefaults');
78+
await scoutSpace.savedObjects.cleanStandardList();
79+
});
80+
81+
spaceTest(
82+
'renders vertical progress bar on dashboard',
83+
async ({ browserAuth, kbnClient, page, pageObjects, scoutSpace }) => {
84+
spaceTest.fail(!storedDataViewId, 'Stored data view was not created in beforeAll');
85+
86+
const dashboardId = await createDashboard(
87+
kbnClient,
88+
{
89+
title: 'Metric progress bar vertical',
90+
time_range: LOGSTASH_TIME_RANGE,
91+
panels: [
92+
{
93+
type: 'vis',
94+
grid: { x: 0, y: 0, w: 12, h: 8 },
95+
config: {
96+
type: 'metric',
97+
title: 'Average bytes with progress bar',
98+
data_source: {
99+
type: 'data_view_reference',
100+
ref_id: storedDataViewId!,
101+
},
102+
metrics: [
103+
{
104+
type: 'primary',
105+
operation: 'average',
106+
field: 'bytes',
107+
background_chart: {
108+
type: 'bar',
109+
max_value: { operation: 'max', field: 'bytes' },
110+
},
111+
},
112+
],
113+
},
114+
},
115+
],
116+
},
117+
scoutSpace.id
118+
);
119+
120+
await browserAuth.loginAsPrivilegedUser();
121+
await pageObjects.dashboard.openDashboardWithId(dashboardId);
122+
await pageObjects.dashboard.waitForPanelsToLoad(1);
123+
await expect(page.testSubj.locator('embeddableError')).toHaveCount(0);
124+
125+
const metricVis = page.getByTestId('mtrVis');
126+
await expect(metricVis).toBeVisible();
127+
128+
await expect(page.locator('.echSingleMetricProgress--vertical')).toBeVisible();
129+
130+
await expect(page.getByRole('meter')).toBeVisible();
131+
}
132+
);
133+
134+
spaceTest(
135+
'renders horizontal progress bar on dashboard',
136+
async ({ browserAuth, kbnClient, page, pageObjects, scoutSpace }) => {
137+
spaceTest.fail(!storedDataViewId, 'Stored data view was not created in beforeAll');
138+
139+
const dashboardId = await createDashboard(
140+
kbnClient,
141+
{
142+
title: 'Metric progress bar horizontal',
143+
time_range: LOGSTASH_TIME_RANGE,
144+
panels: [
145+
{
146+
type: 'vis',
147+
grid: { x: 0, y: 0, w: 12, h: 8 },
148+
config: {
149+
type: 'metric',
150+
title: 'Average bytes horizontal bar',
151+
data_source: {
152+
type: 'data_view_reference',
153+
ref_id: storedDataViewId!,
154+
},
155+
metrics: [
156+
{
157+
type: 'primary',
158+
operation: 'average',
159+
field: 'bytes',
160+
background_chart: {
161+
type: 'bar',
162+
orientation: 'horizontal',
163+
max_value: { operation: 'max', field: 'bytes' },
164+
},
165+
},
166+
],
167+
},
168+
},
169+
],
170+
},
171+
scoutSpace.id
172+
);
173+
174+
await browserAuth.loginAsPrivilegedUser();
175+
await pageObjects.dashboard.openDashboardWithId(dashboardId);
176+
await pageObjects.dashboard.waitForPanelsToLoad(1);
177+
await expect(page.testSubj.locator('embeddableError')).toHaveCount(0);
178+
179+
const metricVis = page.getByTestId('mtrVis');
180+
await expect(metricVis).toBeVisible();
181+
182+
await expect(page.locator('.echSingleMetricProgress--horizontal')).toBeVisible();
183+
184+
await expect(page.getByRole('meter')).toBeVisible();
185+
}
186+
);
187+
188+
spaceTest(
189+
'does not render progress bar when max value is not configured',
190+
async ({ browserAuth, kbnClient, page, pageObjects, scoutSpace }) => {
191+
spaceTest.fail(!storedDataViewId, 'Stored data view was not created in beforeAll');
192+
193+
const dashboardId = await createDashboard(
194+
kbnClient,
195+
{
196+
title: 'Metric without progress bar',
197+
time_range: LOGSTASH_TIME_RANGE,
198+
panels: [
199+
{
200+
type: 'vis',
201+
grid: { x: 0, y: 0, w: 12, h: 8 },
202+
config: {
203+
type: 'metric',
204+
title: 'Average bytes only',
205+
data_source: {
206+
type: 'data_view_reference',
207+
ref_id: storedDataViewId!,
208+
},
209+
metrics: [
210+
{
211+
type: 'primary',
212+
operation: 'average',
213+
field: 'bytes',
214+
},
215+
],
216+
},
217+
},
218+
],
219+
},
220+
scoutSpace.id
221+
);
222+
223+
await browserAuth.loginAsPrivilegedUser();
224+
await pageObjects.dashboard.openDashboardWithId(dashboardId);
225+
await pageObjects.dashboard.waitForPanelsToLoad(1);
226+
await expect(page.testSubj.locator('embeddableError')).toHaveCount(0);
227+
228+
const metricVis = page.getByTestId('mtrVis');
229+
await expect(metricVis).toBeVisible();
230+
await expect(page.locator('.echSingleMetricProgress')).toHaveCount(0);
231+
await expect(page.getByRole('meter')).toHaveCount(0);
232+
}
233+
);
234+
}
235+
);

0 commit comments

Comments
 (0)