[EUI][APM] Update Hardcoded Colors (#203348)

## Summary

This PR replaces a couple of places where hardcoded colors are used in
the APM portion of Kibana with EUITheme colors.
Before & After screenshots can be seen in the associated issue, #200960.
However, I was unable to find an example for the
[.../alert_details_app_section/failed_transaction_chart.tsx](https://github.com/elastic/kibana/pull/203348/files#diff-9d9e4bbfe128f4d2f6ff7f027cf746d679a6c06805ef77240cceb2770a837a28).
It seems like this chart in the alert creation flyout will never render
with annotations.

### Checklist

Check the PR satisfies following conditions. 
Reviewers should verify this PR satisfies this list as well.

- [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)

---------

Co-authored-by: Miriam <31922082+MiriamAparicio@users.noreply.github.com>
This commit is contained in:
Bryce Buchanan 2024-12-12 14:09:17 -08:00 committed by GitHub
parent b3e2b4bf06
commit 278889ab41
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 107 additions and 71 deletions

View file

@ -17,7 +17,6 @@ import {
export const LABELS = 'labels'; // implies labels.* wildcard
export const APM_SERVICE_GROUP_SAVED_OBJECT_TYPE = 'apm-service-group';
export const SERVICE_GROUP_COLOR_DEFAULT = '#D1DAE7';
export const MAX_NUMBER_OF_SERVICE_GROUPS = 500;
export interface ServiceGroup {

View file

@ -8,7 +8,6 @@
import { SettingsSpec } from '@elastic/charts';
export const DEFAULT_DATE_FORMAT = 'HH:mm:ss';
export const CHART_ANNOTATION_RED_COLOR = '#BD271E';
export const CHART_SETTINGS: Partial<SettingsSpec> = {
showLegend: false,
};

View file

@ -7,7 +7,6 @@
/* Error Rate */
import React from 'react';
import chroma from 'chroma-js';
import {
EuiFlexItem,
EuiPanel,
@ -155,7 +154,7 @@ function FailedTransactionChart({
<AlertActiveTimeRangeAnnotation
alertStart={alertStart}
alertEnd={alertEnd}
color={chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase()}
color={transparentize(euiTheme.colors.danger, 0.2)}
id={'alertActiveRect'}
key={'alertActiveRect'}
/>,

View file

@ -13,6 +13,7 @@ import type { Filter } from '@kbn/es-query';
import { ApmPluginStartDeps } from '../../../../../plugin';
import { getLayerList } from './map_layers/get_layer_list';
import { MapTypes } from '../../../../../../common/mobile/constants';
import { StyleColorParams } from './map_layers/style_color_params';
function EmbeddedMapComponent({
selectedMap,
@ -21,6 +22,11 @@ function EmbeddedMapComponent({
kuery = '',
filters,
dataView,
styleColors = {
lineColor: '',
labelColor: '',
labelOutlineColor: '',
},
}: {
selectedMap: MapTypes;
start: string;
@ -28,6 +34,7 @@ function EmbeddedMapComponent({
kuery?: string;
filters: Filter[];
dataView?: DataView;
styleColors?: StyleColorParams;
}) {
const { maps } = useKibana<ApmPluginStartDeps>().services;
@ -37,9 +44,10 @@ function EmbeddedMapComponent({
selectedMap,
maps,
dataViewId: dataView?.id,
styleColors,
})
: [];
}, [selectedMap, maps, dataView?.id]);
}, [selectedMap, maps, dataView?.id, styleColors]);
return (
<div

View file

@ -9,9 +9,11 @@ import React, { useState } from 'react';
import { DataView } from '@kbn/data-views-plugin/common';
import { EuiSpacer } from '@elastic/eui';
import type { Filter } from '@kbn/es-query';
import { useEuiTheme } from '@elastic/eui';
import { EmbeddedMap } from './embedded_map';
import { MapTypes } from '../../../../../../common/mobile/constants';
import { EmbeddedMapSelect } from './embedded_map_select';
import { StyleColorParams } from './map_layers/style_color_params';
export function GeoMap({
start,
@ -27,7 +29,13 @@ export function GeoMap({
dataView?: DataView;
}) {
const [selectedMap, selectMap] = useState(MapTypes.Http);
const { euiTheme } = useEuiTheme();
const styleColors = {
lineColor: euiTheme.colors.textParagraph,
labelColor: euiTheme.colors.textParagraph,
labelOutlineColor: euiTheme.colors.backgroundBasePlain,
} as StyleColorParams;
return (
<>
<EmbeddedMapSelect selectedMap={selectedMap} onChange={selectMap} />
@ -44,6 +52,7 @@ export function GeoMap({
kuery={kuery}
filters={filters}
dataView={dataView}
styleColors={styleColors}
/>
</div>
</>

View file

@ -26,6 +26,7 @@ import {
} from '../../../../../../../common/es_fields/apm';
import { getLayerStyle, PalleteColors } from './get_map_layer_style';
import { MobileSpanSubtype, MobileSpanType } from '../../../../../../../common/mobile/constants';
import { StyleColorParams } from './style_color_params';
interface VectorLayerDescriptor extends BaseVectorLayerDescriptor {
sourceDescriptor: EMSFileSourceDescriptor;
@ -41,7 +42,11 @@ const label = i18n.translate('xpack.apm.serviceOverview.embeddedMap.httpRequests
defaultMessage: 'HTTP requests',
});
export function getHttpRequestsLayerList(maps: MapsStartApi | undefined, dataViewId: string) {
export function getHttpRequestsLayerList(
maps: MapsStartApi | undefined,
dataViewId: string,
styleColors: StyleColorParams
) {
const whereQuery = {
language: 'kuery',
query: `${PROCESSOR_EVENT}:${ProcessorEvent.span} and ${SPAN_SUBTYPE}:${MobileSpanSubtype.Http} and ${SPAN_TYPE}:${MobileSpanType.External}`,
@ -74,7 +79,7 @@ export function getHttpRequestsLayerList(maps: MapsStartApi | undefined, dataVie
id: 'world_countries',
tooltipProperties: [COUNTRY_NAME],
},
style: getLayerStyle(HTTP_REQUEST_PER_COUNTRY, PalleteColors.BluetoRed),
style: getLayerStyle(HTTP_REQUEST_PER_COUNTRY, PalleteColors.BluetoRed, styleColors),
id: uuidv4(),
label: i18n.translate('xpack.apm.serviceOverview.embeddedMap.httpRequests.country.label', {
defaultMessage: 'HTTP requests per country',
@ -113,7 +118,7 @@ export function getHttpRequestsLayerList(maps: MapsStartApi | undefined, dataVie
id: 'administrative_regions_lvl2',
tooltipProperties: ['region_iso_code'],
},
style: getLayerStyle(HTTP_REQUESTS_PER_REGION, PalleteColors.YellowtoRed),
style: getLayerStyle(HTTP_REQUESTS_PER_REGION, PalleteColors.YellowtoRed, styleColors),
id: uuidv4(),
label: i18n.translate('xpack.apm.serviceOverview.embeddedMap.httpRequests.region.label', {
defaultMessage: 'HTTP requests per region',

View file

@ -8,23 +8,26 @@ import type { MapsStartApi } from '@kbn/maps-plugin/public';
import { LayerDescriptor } from '@kbn/maps-plugin/common';
import { getHttpRequestsLayerList } from './get_http_requests_map_layer_list';
import { getSessionMapLayerList } from './get_session_map_layer_list';
import { StyleColorParams } from './style_color_params';
import { MapTypes } from '../../../../../../../common/mobile/constants';
export function getLayerList({
selectedMap,
maps,
dataViewId,
styleColors,
}: {
selectedMap: MapTypes;
maps: MapsStartApi | undefined;
dataViewId: string;
styleColors: StyleColorParams;
}): LayerDescriptor[] {
switch (selectedMap) {
case MapTypes.Http:
return getHttpRequestsLayerList(maps, dataViewId);
return getHttpRequestsLayerList(maps, dataViewId, styleColors);
case MapTypes.Session:
return getSessionMapLayerList(maps, dataViewId);
return getSessionMapLayerList(maps, dataViewId, styleColors);
default:
return getHttpRequestsLayerList(maps, dataViewId);
return getHttpRequestsLayerList(maps, dataViewId, styleColors);
}
}

View file

@ -15,12 +15,19 @@ import {
SYMBOLIZE_AS_TYPES,
} from '@kbn/maps-plugin/common';
import { StyleColorParams } from './style_color_params';
export enum PalleteColors {
BluetoRed = 'Blue to Red',
YellowtoRed = 'Yellow to Red',
}
export function getLayerStyle(fieldName: string, color: PalleteColors): VectorStyleDescriptor {
export function getLayerStyle(
fieldName: string,
color: PalleteColors,
styleColors: StyleColorParams
): VectorStyleDescriptor {
const { lineColor, labelColor, labelOutlineColor } = styleColors;
return {
type: 'VECTOR',
properties: {
@ -41,7 +48,7 @@ export function getLayerStyle(fieldName: string, color: PalleteColors): VectorSt
},
lineColor: {
type: STYLE_TYPE.DYNAMIC,
options: { color: '#3d3d3d', fieldMetaOptions: { isEnabled: true } },
options: { color: lineColor, fieldMetaOptions: { isEnabled: true } },
},
lineWidth: { type: STYLE_TYPE.STATIC, options: { size: 1 } },
iconSize: { type: STYLE_TYPE.STATIC, options: { size: 6 } },
@ -72,12 +79,12 @@ export function getLayerStyle(fieldName: string, color: PalleteColors): VectorSt
},
labelColor: {
type: STYLE_TYPE.STATIC,
options: { color: '#3d3d3d' },
options: { color: labelColor },
},
labelSize: { type: STYLE_TYPE.STATIC, options: { size: 14 } },
labelBorderColor: {
type: STYLE_TYPE.STATIC,
options: { color: '#FFFFFF' },
options: { color: labelOutlineColor },
},
symbolizeAs: { options: { value: SYMBOLIZE_AS_TYPES.CIRCLE } },
labelBorderSize: { options: { size: LABEL_BORDER_SIZES.SMALL } },

View file

@ -22,6 +22,7 @@ import {
SESSION_ID,
} from '../../../../../../../common/es_fields/apm';
import { getLayerStyle, PalleteColors } from './get_map_layer_style';
import { StyleColorParams } from './style_color_params';
interface VectorLayerDescriptor extends BaseVectorLayerDescriptor {
sourceDescriptor: EMSFileSourceDescriptor;
@ -36,7 +37,11 @@ const SESSION_PER_REGION = `__kbnjoin__cardinality_of_session.id__${PER_REGION_L
const label = i18n.translate('xpack.apm.serviceOverview.embeddedMap.session.metric.label', {
defaultMessage: 'Sessions',
});
export function getSessionMapLayerList(maps: MapsStartApi | undefined, dataViewId: string) {
export function getSessionMapLayerList(
maps: MapsStartApi | undefined,
dataViewId: string,
styleColors: StyleColorParams
) {
const sessionsByCountryLayer: VectorLayerDescriptor = {
joins: [
{
@ -64,7 +69,7 @@ export function getSessionMapLayerList(maps: MapsStartApi | undefined, dataViewI
id: 'world_countries',
tooltipProperties: [COUNTRY_NAME],
},
style: getLayerStyle(SESSION_PER_COUNTRY, PalleteColors.BluetoRed),
style: getLayerStyle(SESSION_PER_COUNTRY, PalleteColors.BluetoRed, styleColors),
id: uuidv4(),
label: i18n.translate('xpack.apm.serviceOverview.embeddedMap.sessionCountry.metric.label', {
defaultMessage: 'Sessions per country',
@ -103,7 +108,7 @@ export function getSessionMapLayerList(maps: MapsStartApi | undefined, dataViewI
id: 'administrative_regions_lvl2',
tooltipProperties: ['region_iso_code'],
},
style: getLayerStyle(SESSION_PER_REGION, PalleteColors.YellowtoRed),
style: getLayerStyle(SESSION_PER_REGION, PalleteColors.YellowtoRed, styleColors),
id: uuidv4(),
label: i18n.translate('xpack.apm.serviceOverview.embeddedMap.sessionRegion.metric.label', {
defaultMessage: 'Sessions per region',

View file

@ -0,0 +1,12 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export interface StyleColorParams {
lineColor: string;
labelColor: string;
labelOutlineColor: string;
}

View file

@ -19,6 +19,7 @@ import {
useColorPickerState,
EuiText,
isValidHex,
useEuiTheme,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useEffect, useState } from 'react';
@ -43,7 +44,8 @@ export function GroupDetails({
isLoading,
titleId,
}: Props) {
const initialColor = serviceGroup?.color || '#5094C4';
const { euiTheme } = useEuiTheme();
const initialColor = serviceGroup?.color || euiTheme.colors.backgroundFilledPrimary;
const [name, setName] = useState(serviceGroup?.groupName);
const [color, setColor, colorPickerErrors] = useColorPickerState(initialColor);
const [description, setDescription] = useState<string | undefined>(serviceGroup?.description);

View file

@ -14,10 +14,11 @@ import {
EuiFlexItem,
EuiText,
useIsWithinBreakpoints,
useEuiTheme,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { ServiceGroup, SERVICE_GROUP_COLOR_DEFAULT } from '../../../../../common/service_groups';
import { ServiceGroup } from '../../../../../common/service_groups';
import { useObservabilityActiveAlertsHref } from '../../../shared/links/kibana';
import { ServiceStat } from './service_stat';
@ -30,7 +31,7 @@ interface Props {
export function ServiceGroupsCard({ serviceGroup, href, serviceGroupCounts, isLoading }: Props) {
const isMobile = useIsWithinBreakpoints(['xs', 's']);
const { euiTheme } = useEuiTheme();
const activeAlertsHref = useObservabilityActiveAlertsHref(serviceGroup.kuery);
const cardProps: EuiCardProps = {
style: { width: isMobile ? '100%' : 286 },
@ -38,7 +39,7 @@ export function ServiceGroupsCard({ serviceGroup, href, serviceGroupCounts, isLo
<>
<EuiAvatar
name={serviceGroup.groupName}
color={serviceGroup.color || SERVICE_GROUP_COLOR_DEFAULT}
color={serviceGroup.color || euiTheme.colors.backgroundFilledPrimary}
size="l"
/>
</>

View file

@ -8,52 +8,58 @@
import React from 'react';
import { mount } from 'enzyme';
import { HttpStatusBadge } from '.';
import {
successColor,
neutralColor,
warningColor,
errorColor,
} from '../../../../utils/http_status_code_to_color';
import { renderHook } from '@testing-library/react-hooks';
import { useEuiTheme } from '@elastic/eui';
describe('HttpStatusBadge', () => {
describe('render', () => {
describe('with status code 100', () => {
it('renders with neutral color', () => {
const wrapper = mount(<HttpStatusBadge status={100} />);
expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(neutralColor);
const { result } = renderHook(() => useEuiTheme());
expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(
result.current.euiTheme.colors.vis.euiColorVisGrey0
);
});
});
describe('with status code 200', () => {
it('renders with success color', () => {
const wrapper = mount(<HttpStatusBadge status={200} />);
expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(successColor);
const { result } = renderHook(() => useEuiTheme());
expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(
result.current.euiTheme.colors.vis.euiColorVisSuccess0
);
});
});
describe('with status code 301', () => {
it('renders with neutral color', () => {
const wrapper = mount(<HttpStatusBadge status={301} />);
expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(neutralColor);
const { result } = renderHook(() => useEuiTheme());
expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(
result.current.euiTheme.colors.vis.euiColorVisGrey0
);
});
});
describe('with status code 404', () => {
it('renders with warning color', () => {
const wrapper = mount(<HttpStatusBadge status={404} />);
expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(warningColor);
const { result } = renderHook(() => useEuiTheme());
expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(
result.current.euiTheme.colors.vis.euiColorVisWarning0
);
});
});
describe('with status code 502', () => {
it('renders with error color', () => {
const wrapper = mount(<HttpStatusBadge status={502} />);
expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(errorColor);
const { result } = renderHook(() => useEuiTheme());
expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(
result.current.euiTheme.colors.vis.euiColorVisDanger0
);
});
});

View file

@ -9,7 +9,7 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiToolTip, EuiBadge } from '@elastic/eui';
import { statusCodes } from './status_codes';
import { httpStatusCodeToColor } from '../../../../utils/http_status_code_to_color';
import { useGetStatusColor } from '../../../../utils/http_status_code_to_color';
interface HttpStatusBadgeProps {
status: number;
@ -18,10 +18,9 @@ export function HttpStatusBadge({ status }: HttpStatusBadgeProps) {
const label = i18n.translate('xpack.apm.transactionDetails.statusCode', {
defaultMessage: 'Status code',
});
return (
<EuiToolTip content={label}>
<EuiBadge color={httpStatusCodeToColor(status) || 'default'}>
<EuiBadge color={useGetStatusColor(status) || 'default'}>
{status} {statusCodes[status.toString()]}
</EuiBadge>
</EuiToolTip>

View file

@ -5,35 +5,17 @@
* 2.0.
*/
import { euiLightVars as theme } from '@kbn/ui-theme';
const { euiColorDarkShade, euiColorWarning } = theme;
import { useEuiTheme } from '@elastic/eui';
export const errorColor = '#c23c2b';
export const neutralColor = euiColorDarkShade;
export const successColor = '#327a42';
export const warningColor = euiColorWarning;
const httpStatusCodeColors: Record<string, string> = {
1: neutralColor,
2: successColor,
3: neutralColor,
4: warningColor,
5: errorColor,
export const useGetStatusColor = (status: string | number) => {
const { euiTheme } = useEuiTheme();
const httpStatusCodeColors: Record<string, string> = {
1: euiTheme.colors.vis.euiColorVisGrey0,
2: euiTheme.colors.vis.euiColorVisSuccess0,
3: euiTheme.colors.vis.euiColorVisGrey0,
4: euiTheme.colors.vis.euiColorVisWarning0,
5: euiTheme.colors.vis.euiColorVisDanger0,
};
const intStatus = typeof status === 'string' ? parseInt(status.replace(/\D/g, ''), 10) : status;
return httpStatusCodeColors[intStatus.toString().substring(0, 1)];
};
function getStatusColor(status: number) {
return httpStatusCodeColors[status.toString().substr(0, 1)];
}
/**
* Convert an HTTP status code to a color.
*
* If passed a string, it will remove all non-numeric characters
*/
export function httpStatusCodeToColor(status: string | number) {
if (typeof status === 'string') {
return getStatusColor(parseInt(status.replace(/\D/g, ''), 10));
} else {
return getStatusColor(status);
}
}