diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/grid-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/grid-chrome-linux.png index 84b4979c43..b2f2f5dbc0 100644 Binary files a/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/grid-chrome-linux.png and b/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/grid-chrome-linux.png differ diff --git a/e2e/screenshots/chart.test.ts-snapshots/chart/sizing/should-accommodate-chart-title-and-description-metric-sm-chrome-linux.png b/e2e/screenshots/chart.test.ts-snapshots/chart/sizing/should-accommodate-chart-title-and-description-metric-sm-chrome-linux.png index a117d47769..534ece25ce 100644 Binary files a/e2e/screenshots/chart.test.ts-snapshots/chart/sizing/should-accommodate-chart-title-and-description-metric-sm-chrome-linux.png and b/e2e/screenshots/chart.test.ts-snapshots/chart/sizing/should-accommodate-chart-title-and-description-metric-sm-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-metric-with-transparent-bg-color-dark-theme-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-metric-with-transparent-bg-color-dark-theme-chrome-linux.png new file mode 100644 index 0000000000..6f549ac318 Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-metric-with-transparent-bg-color-dark-theme-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-metric-with-transparent-bg-color-eui-dark-theme-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-metric-with-transparent-bg-color-eui-dark-theme-chrome-linux.png new file mode 100644 index 0000000000..ad0aa52c42 Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-metric-with-transparent-bg-color-eui-dark-theme-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-metric-with-transparent-bg-color-eui-light-theme-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-metric-with-transparent-bg-color-eui-light-theme-chrome-linux.png new file mode 100644 index 0000000000..cbfd9b3b9a Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-metric-with-transparent-bg-color-eui-light-theme-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-metric-with-transparent-bg-color-light-theme-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-metric-with-transparent-bg-color-light-theme-chrome-linux.png new file mode 100644 index 0000000000..cbfd9b3b9a Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-metric-with-transparent-bg-color-light-theme-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-metric-with-transparent-bg-color-dark-theme-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-metric-with-transparent-bg-color-dark-theme-chrome-linux.png new file mode 100644 index 0000000000..4439457778 Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-metric-with-transparent-bg-color-dark-theme-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-metric-with-transparent-bg-color-eui-dark-theme-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-metric-with-transparent-bg-color-eui-dark-theme-chrome-linux.png new file mode 100644 index 0000000000..2b45e504c4 Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-metric-with-transparent-bg-color-eui-dark-theme-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-metric-with-transparent-bg-color-eui-light-theme-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-metric-with-transparent-bg-color-eui-light-theme-chrome-linux.png new file mode 100644 index 0000000000..fabb78a039 Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-metric-with-transparent-bg-color-eui-light-theme-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-metric-with-transparent-bg-color-light-theme-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-metric-with-transparent-bg-color-light-theme-chrome-linux.png new file mode 100644 index 0000000000..fabb78a039 Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-metric-with-transparent-bg-color-light-theme-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-metric-with-transparent-bg-color-dark-theme-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-metric-with-transparent-bg-color-dark-theme-chrome-linux.png new file mode 100644 index 0000000000..11de00a658 Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-metric-with-transparent-bg-color-dark-theme-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-metric-with-transparent-bg-color-eui-dark-theme-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-metric-with-transparent-bg-color-eui-dark-theme-chrome-linux.png new file mode 100644 index 0000000000..1fbe71ab2d Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-metric-with-transparent-bg-color-eui-dark-theme-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-metric-with-transparent-bg-color-eui-light-theme-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-metric-with-transparent-bg-color-eui-light-theme-chrome-linux.png new file mode 100644 index 0000000000..dd2a0d6f2d Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-metric-with-transparent-bg-color-eui-light-theme-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-metric-with-transparent-bg-color-light-theme-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-metric-with-transparent-bg-color-light-theme-chrome-linux.png new file mode 100644 index 0000000000..dd2a0d6f2d Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-metric-with-transparent-bg-color-light-theme-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-horizontal-progress-bar-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-horizontal-progress-bar-chrome-linux.png index 86309bf9bc..0c19e50c31 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-horizontal-progress-bar-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-horizontal-progress-bar-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-horizontal-progress-bar-in-dark-mode-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-horizontal-progress-bar-in-dark-mode-chrome-linux.png index 00f33b0c30..3ce7ab7862 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-horizontal-progress-bar-in-dark-mode-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-horizontal-progress-bar-in-dark-mode-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-no-progress-bar-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-no-progress-bar-chrome-linux.png index 86309bf9bc..0c19e50c31 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-no-progress-bar-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-no-progress-bar-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-no-progress-bar-in-dark-mode-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-no-progress-bar-in-dark-mode-chrome-linux.png index 00f33b0c30..3ce7ab7862 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-no-progress-bar-in-dark-mode-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-no-progress-bar-in-dark-mode-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-vertical-progress-bar-in-dark-mode-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-vertical-progress-bar-in-dark-mode-chrome-linux.png index 7a20df120f..7ec59e9d95 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-vertical-progress-bar-in-dark-mode-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-vertical-progress-bar-in-dark-mode-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/text-value-with-trend-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/text-value-with-trend-chrome-linux.png index 397034a0c8..122918d262 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/text-value-with-trend-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/text-value-with-trend-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/value-icon-and-value-color-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/value-icon-and-value-color-chrome-linux.png index 30a337dd8f..c105a23035 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/value-icon-and-value-color-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/value-icon-and-value-color-chrome-linux.png differ diff --git a/e2e/tests/metric_stories.test.ts b/e2e/tests/metric_stories.test.ts index 51b0d293bf..2e095c4c0a 100644 --- a/e2e/tests/metric_stories.test.ts +++ b/e2e/tests/metric_stories.test.ts @@ -8,6 +8,7 @@ import { test } from '@playwright/test'; +import { eachTheme, pwEach } from '../helpers'; import { common } from '../page_objects'; test.describe('Metric', () => { @@ -21,7 +22,7 @@ test.describe('Metric', () => { 'http://localhost:9001/?path=/story/metric-alpha--grid&globals=theme:eui-light&knob-use progress bar=&knob-progress bar direction=horizontal&knob-max trend data points=30&knob-layout=grid', ); }); - test('should render vertical progress bar in dark mode', async ({ page }) => { + test('should render vertical progress bar in dark mode', async ({ page }) => { await common.expectChartAtUrlToMatchScreenshot(page)( 'http://localhost:9001/?path=/story/metric-alpha--grid&globals=theme:eui-dark&knob-layout=grid&knob-max trend data points=30&knob-progress bar direction=vertical&knob-use progress bar=true', ); @@ -31,14 +32,14 @@ test.describe('Metric', () => { 'http://localhost:9001/?path=/story/metric-alpha--grid&globals=theme:eui-dark&knob-layout=grid&knob-max trend data points=30&knob-progress bar direction=horizontal&knob-use progress bar=true', ); }); - test('should render no progress bar in dark mode', async ({ page }) => { + test('should render no progress bar in dark mode', async ({ page }) => { await common.expectChartAtUrlToMatchScreenshot(page)( 'http://localhost:9001/?path=/story/metric-alpha--grid&globals=theme:eui-dark&knob-layout=grid&knob-max trend data points=30&knob-progress bar direction=horizontal&knob-use progress bar=', ); }); test('text value with trend', async ({ page }) => { await common.expectChartAtUrlToMatchScreenshot(page)( - 'http://localhost:9001/?path=/story/metric-alpha--basic&globals=theme:eui-light&knob-EUI icon glyph name=warning&knob-color=rgba(166, 219, 208, 0.47)&knob-extra=1310 (-74% week before)&knob-is numeric metric=false&knob-progress bar direction=vertical&knob-progress max=100&knob-progress or trend=trend&knob-subtitle=&knob-title=Most used in&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=United States&knob-value postfix=&knob-value prefix=&knob-show icon=', + 'http://localhost:9001/?path=/story/metric-alpha--basic&globals=theme:eui-light&knob-EUI icon glyph name=warning&knob-color=rgba(166, 219, 208, 1)&knob-extra=1310 (-74% week before)&knob-is numeric metric=false&knob-progress bar direction=vertical&knob-progress max=100&knob-progress or trend=trend&knob-subtitle=&knob-title=Most used in&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=United States&knob-value postfix=&knob-value prefix=&knob-show icon=', ); }); test('value icon and value color', async ({ page }) => { @@ -46,4 +47,18 @@ test.describe('Metric', () => { 'http://localhost:9001/?path=/story/metric-alpha--basic&globals=theme:eui-light&knob-title=Network out&knob-subtitle=host: 1dc4e&knob-progress or trend=trend&knob-progress bar direction=vertical&knob-trend data points=30&knob-trend shape=area&knob-trend a11y title=The Cluster CPU Usage trend&knob-trend a11y description=The trend shows a peak of CPU usage in the last 5 minutes&knob-extra=last 5m&knob-progress max=100&knob-is numeric metric=true&knob-value=55.23&knob-value prefix=&knob-value postfix=GB&knob-color=rgba(255, 255, 255, 1)&knob-use value color=true&knob-value color=rgba(189, 0, 0, 1)&knob-show icon=true&knob-EUI icon glyph name=warning&knob-show value icon=true&knob-EUI value icon glyph name=sortUp', ); }); + + pwEach.describe(['trend', 'bar', 'none'])( + (v) => `Metric - ${v} type`, + (type) => { + eachTheme.test( + async ({ page, urlParam }) => { + await common.expectChartAtUrlToMatchScreenshot(page)( + `http://localhost:9001/?path=/story/metric-alpha--basic&${urlParam}&knob-EUI icon glyph name=warning&knob-EUI value icon glyph name=sortUp&knob-color=rgba(157, 66, 66, 0.44)&knob-extra=last 5m&knob-is numeric metric=true&knob-progress bar direction=vertical&knob-progress max=100&knob-progress or trend=${type}&knob-subtitle=Cluster CPU usage&knob-title=21d7f8b7-92ea-41a0-8c03-0db0ec7e11b9&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=55.23&knob-value color=#3c3c3c&knob-value prefix=&knob-value postfix= %&knob-use value color=&knob-show icon=&knob-show value icon=`, + ); + }, + (t) => `should render metric with transparent bg color - ${t} theme`, + ); + }, + ); }); diff --git a/packages/charts/api/charts.api.md b/packages/charts/api/charts.api.md index 15fc2b8631..8928a2f49c 100644 --- a/packages/charts/api/charts.api.md +++ b/packages/charts/api/charts.api.md @@ -1840,8 +1840,6 @@ export type MetricSpecProps = ComponentProps; // @public (undocumented) export interface MetricStyle { - // (undocumented) - background: Color; // (undocumented) barBackground: Color; // (undocumented) diff --git a/packages/charts/src/chart_types/heatmap/layout/viewmodel/viewmodel.ts b/packages/charts/src/chart_types/heatmap/layout/viewmodel/viewmodel.ts index 7dbb839c23..8f76c37e05 100644 --- a/packages/charts/src/chart_types/heatmap/layout/viewmodel/viewmodel.ts +++ b/packages/charts/src/chart_types/heatmap/layout/viewmodel/viewmodel.ts @@ -140,6 +140,7 @@ export function shapeViewModel( const cellHeightInner = cellHeight - gridStrokeWidth; if (colorToRgba(background.color)[3] < 1) { + // TODO: centralize this check and bg color fallback across all chart types Logger.expected( 'Text contrast requires a opaque background color, using fallbackColor', 'an opaque color', @@ -200,7 +201,7 @@ export function shapeViewModel( visible: !isValueInRanges(d.value, bandsToHide), formatted: formattedValue, fontSize, - textColor: fillTextColor(background.fallbackColor, cellBackgroundColor, background.color), + textColor: fillTextColor(background.fallbackColor, cellBackgroundColor, background.color).color.keyword, }); return acc; }, new Map()); diff --git a/packages/charts/src/chart_types/metric/renderer/dom/index.tsx b/packages/charts/src/chart_types/metric/renderer/dom/index.tsx index ea8e197914..f3524603d0 100644 --- a/packages/charts/src/chart_types/metric/renderer/dom/index.tsx +++ b/packages/charts/src/chart_types/metric/renderer/dom/index.tsx @@ -15,9 +15,9 @@ import { connect } from 'react-redux'; import { bindActionCreators, Dispatch } from 'redux'; import { Metric as MetricComponent } from './metric'; -import { highContrastColor } from '../../../../common/color_calcs'; +import { ColorContrastOptions, highContrastColor } from '../../../../common/color_calcs'; import { colorToRgba } from '../../../../common/color_library_wrappers'; -import { Colors } from '../../../../common/colors'; +import { getResolvedBackgroundColor } from '../../../../common/fill_text_color'; import { BasicListener, ElementClickListener, ElementOverListener, settingsBuildProps } from '../../../../specs'; import { onChartRendered } from '../../../../state/actions/chart'; import { GlobalChartState } from '../../../../state/chart_state'; @@ -30,7 +30,7 @@ import { getChartThemeSelector } from '../../../../state/selectors/get_chart_the import { getInternalIsInitializedSelector, InitStatus } from '../../../../state/selectors/get_internal_is_intialized'; import { getSettingsSpecSelector } from '../../../../state/selectors/get_settings_spec'; import { LIGHT_THEME } from '../../../../utils/themes/light_theme'; -import { MetricStyle } from '../../../../utils/themes/theme'; +import { BackgroundStyle, MetricStyle } from '../../../../utils/themes/theme'; import { MetricSpec } from '../../specs'; import { chartSize } from '../../state/selectors/chart_size'; import { getMetricSpecs } from '../../state/selectors/data'; @@ -47,6 +47,7 @@ interface StateProps { specs: MetricSpec[]; a11y: A11ySettings; style: MetricStyle; + background: BackgroundStyle; locale: string; onElementClick?: ElementClickListener; onElementOut?: BasicListener; @@ -76,6 +77,7 @@ class Component extends React.Component { a11y, specs: [spec], // ignoring other specs style, + background, onElementClick, onElementOut, onElementOver, @@ -93,11 +95,12 @@ class Component extends React.Component { }, 0); const panel = { width: width / maxColumns, height: height / totalRows }; - - const emptyForegroundColor = - highContrastColor(colorToRgba(style.background)) === Colors.White.rgba - ? style.text.lightColor - : style.text.darkColor; + const backgroundColor = getResolvedBackgroundColor(background.fallbackColor, background.color); + const contrastOptions: ColorContrastOptions = { + lightColor: colorToRgba(style.text.lightColor), + darkColor: colorToRgba(style.text.darkColor), + }; + const { color: emptyForegroundColor } = highContrastColor(colorToRgba(backgroundColor), undefined, contrastOptions); return ( // eslint-disable-next-line jsx-a11y/no-redundant-roles @@ -123,7 +126,7 @@ class Component extends React.Component { return !datum ? (
  • -
    +
  • ) : ( @@ -138,6 +141,8 @@ class Component extends React.Component { columnIndex={columnIndex} panel={panel} style={style} + backgroundColor={backgroundColor} + contrastOptions={contrastOptions} onElementClick={onElementClick} onElementOut={onElementOut} onElementOver={onElementOver} @@ -185,6 +190,7 @@ const DEFAULT_PROPS: StateProps = { }, a11y: DEFAULT_A11Y_SETTINGS, style: LIGHT_THEME.metric, + background: LIGHT_THEME.background, locale: settingsBuildProps.defaults.locale, }; @@ -193,6 +199,7 @@ const mapStateToProps = (state: GlobalChartState): StateProps => { return DEFAULT_PROPS; } const { onElementClick, onElementOut, onElementOver, locale } = getSettingsSpecSelector(state); + const { metric: style, background } = getChartThemeSelector(state); return { initialized: true, chartId: state.chartId, @@ -203,7 +210,8 @@ const mapStateToProps = (state: GlobalChartState): StateProps => { onElementClick, onElementOver, onElementOut, - style: getChartThemeSelector(state).metric, + background, + style, locale, }; }; diff --git a/packages/charts/src/chart_types/metric/renderer/dom/metric.tsx b/packages/charts/src/chart_types/metric/renderer/dom/metric.tsx index b745ddf306..e69f0249b8 100644 --- a/packages/charts/src/chart_types/metric/renderer/dom/metric.tsx +++ b/packages/charts/src/chart_types/metric/renderer/dom/metric.tsx @@ -10,12 +10,13 @@ import classNames from 'classnames'; import React, { CSSProperties, useState } from 'react'; import { ProgressBar } from './progress'; -import { SparkLine } from './sparkline'; +import { SparkLine, getSparkLineColor } from './sparkline'; import { MetricText } from './text'; -import { highContrastColor } from '../../../../common/color_calcs'; -import { changeColorLightness, colorToRgba } from '../../../../common/color_library_wrappers'; -import { Colors } from '../../../../common/colors'; +import { ColorContrastOptions } from '../../../../common/color_calcs'; +import { changeColorLightness } from '../../../../common/color_library_wrappers'; +import { Color } from '../../../../common/colors'; import { DEFAULT_CSS_CURSOR } from '../../../../common/constants'; +import { fillTextColor } from '../../../../common/fill_text_color'; import { BasicListener, ElementClickListener, @@ -39,6 +40,8 @@ export const Metric: React.FunctionComponent<{ datum: MetricDatum; panel: Size; style: MetricStyle; + backgroundColor: Color; + contrastOptions: ColorContrastOptions; locale: string; onElementClick?: ElementClickListener; onElementOver?: ElementOverListener; @@ -53,6 +56,8 @@ export const Metric: React.FunctionComponent<{ datum, panel, style, + backgroundColor, + contrastOptions, locale, onElementClick, onElementOver, @@ -75,16 +80,11 @@ export const Metric: React.FunctionComponent<{ const lightnessAmount = mouseState === 'leave' ? 0 : mouseState === 'enter' ? 0.05 : 0.1; const interactionColor = changeColorLightness(datum.color, lightnessAmount, 0.8); - const backgroundInteractionColor = changeColorLightness(style.background, lightnessAmount, 0.8); const datumWithInteractionColor: MetricDatum = { ...datum, color: interactionColor, }; - const updatedStyle: MetricStyle = { - ...style, - background: backgroundInteractionColor, - }; const event: MetricElementEvent = { type: 'metricElementEvent', rowIndex, columnIndex }; @@ -92,17 +92,35 @@ export const Metric: React.FunctionComponent<{ backgroundColor: !isMetricWTrend(datumWithInteractionColor) && !isMetricWProgress(datumWithInteractionColor) ? datumWithInteractionColor.color - : updatedStyle.background, + : undefined, cursor: onElementClick ? 'pointer' : DEFAULT_CSS_CURSOR, borderColor: style.border, }; - const bgColor = isMetricWTrend(datum) || !isMetricWProgress(datum) ? datum.color : style.background; + const highContrastTextColor = fillTextColor( + backgroundColor, + isMetricWProgress(datum) ? backgroundColor : datum.color, + undefined, + contrastOptions, + ); + let finalTextColor = highContrastTextColor.color; + + if (isMetricWTrend(datum)) { + const { ratio, color, shade } = fillTextColor( + backgroundColor, + getSparkLineColor(datum.color), + undefined, + contrastOptions, + ); - const highContrastTextColor = - highContrastColor(colorToRgba(bgColor)) === Colors.White.rgba ? style.text.lightColor : style.text.darkColor; + // TODO verify this check is applied correctly + if (shade !== highContrastTextColor.shade && ratio > highContrastTextColor.ratio) { + finalTextColor = color; + } + } const onElementClickHandler = () => onElementClick && onElementClick([event]); + return ( // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions,jsx-a11y/click-events-have-key-events
    {isMetricWTrend(datumWithInteractionColor) && } {isMetricWProgress(datumWithInteractionColor) && ( - + )} -
    +
    ); }; diff --git a/packages/charts/src/chart_types/metric/renderer/dom/sparkline.tsx b/packages/charts/src/chart_types/metric/renderer/dom/sparkline.tsx index dbffab4f61..d83e17bb16 100644 --- a/packages/charts/src/chart_types/metric/renderer/dom/sparkline.tsx +++ b/packages/charts/src/chart_types/metric/renderer/dom/sparkline.tsx @@ -15,6 +15,12 @@ import { isFiniteNumber } from '../../../../utils/common'; import { CurveType } from '../../../../utils/curves'; import { MetricTrendShape, MetricWTrend } from '../../specs'; +/** @internal */ +export const getSparkLineColor = (color: MetricWTrend['color']) => { + const [h, s, l, a] = colorToHsl(color); + return hslToColor(h, s, l >= 0.8 ? l - 0.1 : l + 0.1, a); +}; + /** @internal */ export const SparkLine: FunctionComponent<{ id: string; @@ -36,8 +42,6 @@ export const SparkLine: FunctionComponent<{ trendShape === MetricTrendShape.Bars ? CurveType.CURVE_STEP_AFTER : CurveType.LINEAR, ); - const [h, s, l] = colorToHsl(color); - const pathColor = hslToColor(h, s, l >= 0.8 ? l - 0.1 : l + 0.1); const titleId = `${id}-trend-title`; const descriptionId = `${id}-trend-description`; return ( @@ -51,17 +55,31 @@ export const SparkLine: FunctionComponent<{ role="img" aria-labelledby={`${titleId} ${descriptionId}`} > + + + + + + + {trendA11yTitle} {trendA11yDescription} - + + diff --git a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/fill_text_layout.test.ts b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/fill_text_layout.test.ts index 9d64e7c680..02f19e9476 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/fill_text_layout.test.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/fill_text_layout.test.ts @@ -268,6 +268,6 @@ describe('Test fillTextColor function', () => { const fillColor = 'rgba(55, 126, 184, 0.7)'; const containerBackgroundColor = 'white'; const expectedAdjustedTextColor = 'rgba(0, 0, 0, 1)'; // with WCAG 2 is black - expect(fillTextColor(fillColor, containerBackgroundColor)).toEqual(expectedAdjustedTextColor); + expect(fillTextColor(fillColor, containerBackgroundColor).color.keyword).toEqual(expectedAdjustedTextColor); }); }); diff --git a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/link_text_layout.ts b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/link_text_layout.ts index a9afa69cbe..347dfb11b1 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/link_text_layout.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/link_text_layout.ts @@ -90,7 +90,7 @@ export function linkTextLayout( } const textColor = linkLabel.textColor === ColorVariant.Adaptive - ? fillTextColor(fallbackBGColor, null, backgroundColor) + ? fillTextColor(fallbackBGColor, null, backgroundColor).color.keyword : linkLabel.textColor; const labelFontSpec: Font = { ...linkLabel, textColor }; const valueFontSpec: Font = { ...linkLabel, ...linkLabel.valueFont, textColor }; diff --git a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts index 1c2572aff6..1a0df66497 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/viewmodel.ts @@ -170,7 +170,7 @@ export function makeQuadViewModel( const textColor = textNegligible ? Colors.Transparent.keyword : fillLabel.textColor === ColorVariant.Adaptive - ? fillTextColor(fallbackBGColor, fillColor, backgroundColor) + ? fillTextColor(fallbackBGColor, fillColor, backgroundColor).color.keyword : fillLabel.textColor; return { index, innerIndex, smAccessorValue, strokeWidth, strokeStyle, fillColor, textColor, ...node }; diff --git a/packages/charts/src/chart_types/xy_chart/renderer/canvas/values/bar.ts b/packages/charts/src/chart_types/xy_chart/renderer/canvas/values/bar.ts index be91dd8222..754a944814 100644 --- a/packages/charts/src/chart_types/xy_chart/renderer/canvas/values/bar.ts +++ b/packages/charts/src/chart_types/xy_chart/renderer/canvas/values/bar.ts @@ -204,8 +204,8 @@ function getTextColors( shadowColor: fillDefinition.borderColor || Colors.Transparent.keyword, }; } - const fillColor = fillTextColor(fallbackBGColor, geometryColor, backgroundColor); - const shadowColor = fillTextColor(fallbackBGColor, fillColor, backgroundColor); + const fillColor = fillTextColor(fallbackBGColor, geometryColor, backgroundColor).color.keyword; + const shadowColor = fillTextColor(fallbackBGColor, fillColor, backgroundColor).color.keyword; return { fillColor, diff --git a/packages/charts/src/common/color_calcs.test.ts b/packages/charts/src/common/color_calcs.test.ts index 62db2ec2ac..c452a57aa9 100644 --- a/packages/charts/src/common/color_calcs.test.ts +++ b/packages/charts/src/common/color_calcs.test.ts @@ -17,8 +17,8 @@ describe('Color calcs', () => { const background: RgbaTuple = [120, 116, 178, 0.7]; const blendedBackground: RgbaTuple = [161, 158, 201, 1]; expect(combineColors(background, Colors.White.rgba)).toEqual(blendedBackground); - expect(highContrastColor(blendedBackground, 'WCAG2')).toEqual(Colors.Black.rgba); - expect(highContrastColor(blendedBackground, 'WCAG3')).toEqual(Colors.White.rgba); + expect(highContrastColor(blendedBackground, 'WCAG2').color.rgba).toEqual(Colors.Black.rgba); + expect(highContrastColor(blendedBackground, 'WCAG3').color.rgba).toEqual(Colors.White.rgba); }); }); describe('test the combineColors function', () => { diff --git a/packages/charts/src/common/color_calcs.ts b/packages/charts/src/common/color_calcs.ts index 982504e3bb..a5a02c8866 100644 --- a/packages/charts/src/common/color_calcs.ts +++ b/packages/charts/src/common/color_calcs.ts @@ -6,9 +6,11 @@ * Side Public License, v 1. */ +import { Required } from 'utility-types'; + import { APCAContrast } from './apca_color_contrast'; import { RgbaTuple, RGBATupleToString, RgbTuple } from './color_library_wrappers'; -import { Colors } from './colors'; +import { ColorDefinition, Colors } from './colors'; import { getWCAG2ContrastRatio } from './wcag2_color_contrast'; /** @internal */ @@ -45,16 +47,63 @@ export function combineColors([fgR, fgG, fgB, fgA]: RgbaTuple, [bgR, bgG, bgB, b return [r, g, b, alpha]; } -function getHighContrastColorWCAG2(background: RgbTuple): RgbaTuple { - const wWhite = getWCAG2ContrastRatio(Colors.White.rgba, background); - const wBlack = getWCAG2ContrastRatio(Colors.Black.rgba, background); - return wWhite >= wBlack ? Colors.White.rgba : Colors.Black.rgba; +/** @internal */ +export interface ColorContrastOptions { + darkColor?: RgbaTuple; + lightColor?: RgbaTuple; } -function getHighContrastColorAPCA(background: RgbTuple): RgbaTuple { - const wWhiteText = Math.abs(APCAContrast(background, Colors.White.rgba)); - const wBlackText = Math.abs(APCAContrast(background, Colors.Black.rgba)); - return wWhiteText > wBlackText ? Colors.White.rgba : Colors.Black.rgba; +const getOptionWithDefaults = (options: ColorContrastOptions = {}): Required => ({ + darkColor: Colors.Black.rgba, + lightColor: Colors.White.rgba, + ...options, +}); + +function getHighContrastColorWCAG2(background: RgbTuple, options: ColorContrastOptions = {}): HighContrastResult { + const { lightColor, darkColor } = getOptionWithDefaults(options); + const wLight = getWCAG2ContrastRatio(lightColor, background); + const wDark = getWCAG2ContrastRatio(darkColor, background); + return wLight >= wDark + ? { + color: { + rgba: lightColor, + keyword: RGBATupleToString(lightColor), + }, + ratio: wLight, + shade: 'light', + } + : { + color: { + rgba: darkColor, + keyword: RGBATupleToString(darkColor), + }, + ratio: wDark, + shade: 'dark', + }; +} + +function getHighContrastColorAPCA(background: RgbTuple, options: ColorContrastOptions = {}): HighContrastResult { + const { lightColor, darkColor } = getOptionWithDefaults(options); + const wLightText = Math.abs(APCAContrast(background, lightColor)); + const wDarkText = Math.abs(APCAContrast(background, darkColor)); + + return wLightText > wDarkText + ? { + color: { + rgba: lightColor, + keyword: RGBATupleToString(lightColor), + }, + ratio: wLightText, + shade: 'light', + } + : { + color: { + rgba: darkColor, + keyword: RGBATupleToString(darkColor), + }, + ratio: wDarkText, + shade: 'dark', + }; } const HIGH_CONTRAST_FN = { @@ -62,10 +111,21 @@ const HIGH_CONTRAST_FN = { WCAG3: getHighContrastColorAPCA, }; +/** @internal */ +export interface HighContrastResult { + color: ColorDefinition; + ratio: number; + shade: 'light' | 'dark'; +} + /** * Use white or black text depending on the high contrast mode used * @internal */ -export function highContrastColor(background: RgbTuple, mode: keyof typeof HIGH_CONTRAST_FN = 'WCAG2'): RgbaTuple { - return HIGH_CONTRAST_FN[mode](background); +export function highContrastColor( + background: RgbTuple, + mode: keyof typeof HIGH_CONTRAST_FN = 'WCAG2', + options?: ColorContrastOptions, +): HighContrastResult { + return HIGH_CONTRAST_FN[mode](background, options); } diff --git a/packages/charts/src/common/color_library_wrappers.ts b/packages/charts/src/common/color_library_wrappers.ts index 806811d792..da4309b1f0 100644 --- a/packages/charts/src/common/color_library_wrappers.ts +++ b/packages/charts/src/common/color_library_wrappers.ts @@ -83,18 +83,20 @@ export function colorToRgba(color: Color): RgbaTuple { } /** @internal */ -export function colorToHsl(color: Color) { - const [r, g, b] = colorToRgba(color); - return chroma.rgb(r, g, b).hsl(); +export function colorToHsl(color: Color): [h: number, s: number, l: number, a: number] { + const [r, g, b, a] = colorToRgba(color); + const [h, s, l] = chroma.rgb(r, g, b).hsl(); // alpha not preserved + return [h, s, l, a]; } + /** @internal */ -export function hslToColor(h: number, s: number, l: number): Color { - const rgba = chroma.hsl(h, s, l).rgba(); +export function hslToColor(h: number, s: number, l: number, a = 1): Color { + const rgba = chroma.hsl(h, s, l).alpha(a).rgba(); return RGBATupleToString(rgba); } /** @internal */ export function changeColorLightness(color: Color, lightnessAmount: number, lightnessThreshold: number): Color { - const [h, s, l] = colorToHsl(color); - return hslToColor(h, s, l >= lightnessThreshold ? l - lightnessAmount : l + lightnessAmount); + const [h, s, l, a] = colorToHsl(color); + return hslToColor(h, s, l >= lightnessThreshold ? l - lightnessAmount : l + lightnessAmount, a); } diff --git a/packages/charts/src/common/colors.tsx b/packages/charts/src/common/colors.tsx index d4a6b7834e..332379c78d 100644 --- a/packages/charts/src/common/colors.tsx +++ b/packages/charts/src/common/colors.tsx @@ -15,10 +15,13 @@ import { RgbaTuple } from './color_library_wrappers'; export type Color = string; // todo static/runtime type it this for proper color string content; several places in the code, and ultimate use, dictate it not be an empty string /** @internal */ -export const Colors: Record< - 'Red' | 'White' | 'Black' | 'Transparent' | 'DarkOpaqueRed', - { keyword: Color; rgba: RgbaTuple } -> = { +export interface ColorDefinition { + keyword: Color; + rgba: RgbaTuple; +} + +/** @internal */ +export const Colors: Record<'Red' | 'White' | 'Black' | 'Transparent' | 'DarkOpaqueRed', ColorDefinition> = { Red: { keyword: 'red', rgba: [255, 0, 0, 1], diff --git a/packages/charts/src/common/fill_text_color.test.ts b/packages/charts/src/common/fill_text_color.test.ts index 38078e18ce..af2e8dfc96 100644 --- a/packages/charts/src/common/fill_text_color.test.ts +++ b/packages/charts/src/common/fill_text_color.test.ts @@ -13,19 +13,19 @@ import { fillTextColor, TRANSPARENT_LIMIT } from './fill_text_color'; describe('Fill text color', () => { describe('highContrastColor', () => { it('should return black when background is white', () => { - expect(fillTextColor(Colors.White.keyword, null, Colors.White.keyword)).toEqual( + expect(fillTextColor(Colors.White.keyword, null, Colors.White.keyword).color.keyword).toEqual( RGBATupleToString(Colors.Black.rgba), ); }); // test contrast computation it('should return black with yellow/semi-transparent background', () => { - expect(fillTextColor(Colors.White.keyword, null, 'rgba(255,255,51,0.3)')).toEqual( + expect(fillTextColor(Colors.White.keyword, null, 'rgba(255,255,51,0.3)').color.keyword).toEqual( RGBATupleToString(Colors.Black.rgba), ); }); it('should use white text for Thailand color', () => { // black for WCAG2, white for WCAG3 - expect(fillTextColor(Colors.White.keyword, null, 'rgba(120, 116, 178, 1)')).toEqual( + expect(fillTextColor(Colors.White.keyword, null, 'rgba(120, 116, 178, 1)').color.keyword).toEqual( RGBATupleToString(Colors.Black.rgba), ); }); @@ -35,29 +35,32 @@ describe('Fill text color', () => { const fallbackBG = Colors.White.keyword; it('should use fallbackBG if background is transparent and no foreground', () => { - expect(fillTextColor(fallbackBG, null, Colors.Transparent.keyword)).toEqual('rgba(0, 0, 0, 1)'); + expect(fillTextColor(fallbackBG, null, Colors.Transparent.keyword).color.keyword).toEqual('rgba(0, 0, 0, 1)'); }); it('should use fallbackBG if background is lower tranparent limit and no foreground', () => { - expect(fillTextColor(fallbackBG, null, `rgba(120, 116, 178, ${TRANSPARENT_LIMIT - Number.EPSILON})`)).toEqual( - 'rgba(0, 0, 0, 1)', - ); + expect( + fillTextColor(fallbackBG, null, `rgba(120, 116, 178, ${TRANSPARENT_LIMIT - Number.EPSILON})`).color.keyword, + ).toEqual('rgba(0, 0, 0, 1)'); }); it('should contrast fallbackBG with foreground if background is transparent', () => { - expect(fillTextColor(fallbackBG, 'rgba(0, 0, 255, 1)', Colors.Transparent.keyword)).toEqual( + expect(fillTextColor(fallbackBG, 'rgba(0, 0, 255, 1)', Colors.Transparent.keyword).color.keyword).toEqual( 'rgba(255, 255, 255, 1)', ); }); it('should contrast fallbackBG with transparent foreground if background is transparent', () => { expect( - fillTextColor(fallbackBG, `rgba(0, 0, 255, ${TRANSPARENT_LIMIT - Number.EPSILON})`, Colors.Transparent.keyword), + fillTextColor(fallbackBG, `rgba(0, 0, 255, ${TRANSPARENT_LIMIT - Number.EPSILON})`, Colors.Transparent.keyword) + .color.keyword, ).toEqual('rgba(0, 0, 0, 1)'); }); it('should return constrast with opac foreground and opac background', () => { - expect(fillTextColor(fallbackBG, 'rgba(0, 0, 255, 1)', 'rgba(255, 0, 0, 1)')).toEqual('rgba(255, 255, 255, 1)'); + expect(fillTextColor(fallbackBG, 'rgba(0, 0, 255, 1)', 'rgba(255, 0, 0, 1)').color.keyword).toEqual( + 'rgba(255, 255, 255, 1)', + ); }); }); }); diff --git a/packages/charts/src/common/fill_text_color.ts b/packages/charts/src/common/fill_text_color.ts index 62c242ba54..68b2371264 100644 --- a/packages/charts/src/common/fill_text_color.ts +++ b/packages/charts/src/common/fill_text_color.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { combineColors, highContrastColor } from './color_calcs'; +import { ColorContrastOptions, HighContrastResult, combineColors, highContrastColor } from './color_calcs'; import { colorToRgba, RGBATupleToString } from './color_library_wrappers'; import { Color, Colors } from './colors'; @@ -26,7 +26,8 @@ export function fillTextColor( fallbackBGColor: Color, foreground: Color | null, background: Color = Colors.Transparent.keyword, -): Color { + options?: ColorContrastOptions, +): HighContrastResult { let backgroundRGBA = colorToRgba(background); if (backgroundRGBA[3] < TRANSPARENT_LIMIT) { @@ -36,8 +37,22 @@ export function fillTextColor( if (foreground) { const foregroundRGBA = colorToRgba(foreground); const blendedFgBg = combineColors(foregroundRGBA, backgroundRGBA); - return RGBATupleToString(highContrastColor(blendedFgBg)); + return highContrastColor(blendedFgBg, 'WCAG2', options); + } + + return highContrastColor(backgroundRGBA); +} + +/** @internal */ +export function getResolvedBackgroundColor( + fallbackBGColor: Color, + background: Color = Colors.Transparent.keyword, +): Color { + let backgroundRGBA = colorToRgba(background); + + if (backgroundRGBA[3] < TRANSPARENT_LIMIT) { + backgroundRGBA = colorToRgba(fallbackBGColor); } - return RGBATupleToString(highContrastColor(backgroundRGBA)); + return RGBATupleToString(backgroundRGBA); } diff --git a/packages/charts/src/components/tooltip/components/tooltip_table_color_cell.tsx b/packages/charts/src/components/tooltip/components/tooltip_table_color_cell.tsx index 2524ffb739..2b471c0c9f 100644 --- a/packages/charts/src/components/tooltip/components/tooltip_table_color_cell.tsx +++ b/packages/charts/src/components/tooltip/components/tooltip_table_color_cell.tsx @@ -12,7 +12,7 @@ import React from 'react'; import { useTooltipContext } from './tooltip_provider'; import { TooltipTableCell, TooltipTableCellProps } from './tooltip_table_cell'; import { combineColors, highContrastColor } from '../../../common/color_calcs'; -import { colorToRgba, RGBATupleToString } from '../../../common/color_library_wrappers'; +import { colorToRgba } from '../../../common/color_library_wrappers'; import { Color, Colors } from '../../../common/colors'; /** @public */ @@ -42,7 +42,7 @@ export function TooltipTableColorCell({ const foregroundRGBA = colorToRgba(stripColor === Colors.Transparent.keyword ? backgroundColor : stripColor); const backgroundRGBA = colorToRgba(backgroundColor); const blendedFgBg = combineColors(foregroundRGBA, backgroundRGBA); - return RGBATupleToString(highContrastColor(blendedFgBg, 'WCAG3')); + return highContrastColor(blendedFgBg, 'WCAG3').color.keyword; }; const renderColorStrip = () => { diff --git a/packages/charts/src/utils/themes/dark_theme.ts b/packages/charts/src/utils/themes/dark_theme.ts index 3dfb1cd5fb..ca3c2e50d8 100644 --- a/packages/charts/src/utils/themes/dark_theme.ts +++ b/packages/charts/src/utils/themes/dark_theme.ts @@ -410,7 +410,6 @@ export const DARK_THEME: Theme = { }, border: '#343741', barBackground: '#343741', - background: '#1D1E23', nonFiniteText: 'N/A', minHeight: 64, }, diff --git a/packages/charts/src/utils/themes/light_theme.ts b/packages/charts/src/utils/themes/light_theme.ts index 4e88e8b8d5..01e44d3023 100644 --- a/packages/charts/src/utils/themes/light_theme.ts +++ b/packages/charts/src/utils/themes/light_theme.ts @@ -409,7 +409,6 @@ export const LIGHT_THEME: Theme = { }, border: '#EDF0F5', barBackground: '#EDF0F5', - background: '#FFFFFF', nonFiniteText: 'N/A', minHeight: 64, }, diff --git a/packages/charts/src/utils/themes/theme.ts b/packages/charts/src/utils/themes/theme.ts index 23f844f75a..b4db27c0f5 100644 --- a/packages/charts/src/utils/themes/theme.ts +++ b/packages/charts/src/utils/themes/theme.ts @@ -312,7 +312,6 @@ export interface MetricStyle { lightColor: Color; }; border: Color; - background: Color; barBackground: Color; nonFiniteText: string; minHeight: Pixels;