diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/array-of-values-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/array-of-values-chrome-linux.png index 1a24de9e641..5b2699f559e 100644 Binary files a/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/array-of-values-chrome-linux.png and b/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/array-of-values-chrome-linux.png differ diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/basic-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/basic-chrome-linux.png index 8830e5b9d05..463fbcbf72f 100644 Binary files a/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/basic-chrome-linux.png and b/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/basic-chrome-linux.png differ diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/extra-badges-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/extra-badges-chrome-linux.png index 099ebfadce2..e628a7355df 100644 Binary files a/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/extra-badges-chrome-linux.png and b/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/extra-badges-chrome-linux.png differ 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 d0104392c17..c385d0a04f0 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/all.test.ts-snapshots/baselines/metric-alpha/layout-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/layout-chrome-linux.png new file mode 100644 index 00000000000..18a1c0044df Binary files /dev/null and b/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/layout-chrome-linux.png differ diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/trend-multiple-data-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/trend-multiple-data-chrome-linux.png index 69bcb89d42f..d01caf9713f 100644 Binary files a/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/trend-multiple-data-chrome-linux.png and b/e2e/screenshots/all.test.ts-snapshots/baselines/metric-alpha/trend-multiple-data-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-negative-values-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-negative-values-chrome-linux.png index 6121fa5cccc..2fe5d09729c 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-negative-values-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-negative-values-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-negative-values-reversed-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-negative-values-reversed-chrome-linux.png index 1daf14b03ed..40e18901afd 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-negative-values-reversed-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-negative-values-reversed-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-negative-values-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-negative-values-chrome-linux.png index 5f5f9e01ac9..c7f04b9061d 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-negative-values-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-negative-values-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-negative-values-reversed-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-negative-values-reversed-chrome-linux.png index 6b219f277f2..a2fa055e256 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-negative-values-reversed-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-negative-values-reversed-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-values-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-values-chrome-linux.png index 9879bcaa991..db03d364748 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-values-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-values-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-values-reversed-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-values-reversed-chrome-linux.png index b88f6f8b619..97b0185e9b4 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-values-reversed-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-horizontal/bullet-as-metric/should-render-with-positive-values-reversed-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-negative-values-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-negative-values-chrome-linux.png index ef2d1d52f5d..0d11cbd8628 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-negative-values-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-negative-values-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-negative-values-reversed-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-negative-values-reversed-chrome-linux.png index 9ef9c6d2d08..4d7dc42e99a 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-negative-values-reversed-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-negative-values-reversed-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-negative-values-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-negative-values-chrome-linux.png index e2a92cf9a95..7d71fafa084 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-negative-values-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-negative-values-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-negative-values-reversed-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-negative-values-reversed-chrome-linux.png index d63ae223904..0f19aee6c00 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-negative-values-reversed-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-negative-values-reversed-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-values-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-values-chrome-linux.png index 0d0d9abfe81..ce46c5d2242 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-values-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-values-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-values-reversed-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-values-reversed-chrome-linux.png index 5c1f06485c5..abbb113513c 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-values-reversed-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-two-thirds-circle/bullet-as-metric/should-render-with-positive-values-reversed-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-negative-values-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-negative-values-chrome-linux.png index efb6a7512c2..b551190a863 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-negative-values-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-negative-values-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-negative-values-reversed-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-negative-values-reversed-chrome-linux.png index c3f64bfc763..91c5c626d76 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-negative-values-reversed-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-negative-values-reversed-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-negative-values-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-negative-values-chrome-linux.png index c12d752804f..412b0b6dcef 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-negative-values-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-negative-values-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-negative-values-reversed-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-negative-values-reversed-chrome-linux.png index 17befa2defa..907b05dfbd1 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-negative-values-reversed-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-negative-values-reversed-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-values-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-values-chrome-linux.png index d3af90a6149..65acef13217 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-values-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-values-chrome-linux.png differ diff --git a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-values-reversed-chrome-linux.png b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-values-reversed-chrome-linux.png index 295b3812378..1071823c130 100644 Binary files a/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-values-reversed-chrome-linux.png and b/e2e/screenshots/bullet_stories.test.ts-snapshots/bullet-stories/bullet-as-metric-vertical/bullet-as-metric/should-render-with-positive-values-reversed-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 e71ee9a3486..699a67b44c6 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/extra-badge-props/extra-badge-with-big-breakpoints-with-trend-both-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-big-breakpoints-with-trend-both-chrome-linux.png index ef8790bb712..7ab27171417 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-big-breakpoints-with-trend-both-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-big-breakpoints-with-trend-both-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-big-breakpoints-with-trend-icon-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-big-breakpoints-with-trend-icon-chrome-linux.png index 85ee1f02d88..76cba50b2e6 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-big-breakpoints-with-trend-icon-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-big-breakpoints-with-trend-icon-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-big-breakpoints-with-trend-value-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-big-breakpoints-with-trend-value-chrome-linux.png index 5b801207f3c..36ffd7a41b7 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-big-breakpoints-with-trend-value-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-big-breakpoints-with-trend-value-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-small-breakpoints-with-trend-both-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-small-breakpoints-with-trend-both-chrome-linux.png index 099ebfadce2..e628a7355df 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-small-breakpoints-with-trend-both-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-small-breakpoints-with-trend-both-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-small-breakpoints-with-trend-icon-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-small-breakpoints-with-trend-icon-chrome-linux.png index fdd40e96fcb..fa847d087ef 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-small-breakpoints-with-trend-icon-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-small-breakpoints-with-trend-icon-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-small-breakpoints-with-trend-value-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-small-breakpoints-with-trend-value-chrome-linux.png index 730e885bc1a..b26a5c67cc8 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-small-breakpoints-with-trend-value-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/extra-badge-props/extra-badge-with-small-breakpoints-with-trend-value-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-default-secondary-and-after-secondary-label-position-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-default-secondary-and-after-secondary-label-position-chrome-linux.png new file mode 100644 index 00000000000..04c5f9ea105 Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-default-secondary-and-after-secondary-label-position-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-default-secondary-and-before-secondary-label-position-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-default-secondary-and-before-secondary-label-position-chrome-linux.png new file mode 100644 index 00000000000..867daed7ce0 Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-default-secondary-and-before-secondary-label-position-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-default-secondary-metric-with-badge-and-no-secondary-label-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-default-secondary-metric-with-badge-and-no-secondary-label-chrome-linux.png new file mode 100644 index 00000000000..bf20c44e5a7 Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-default-secondary-metric-with-badge-and-no-secondary-label-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-default-secondary-metric-with-no-badge-and-no-secondary-label-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-default-secondary-metric-with-no-badge-and-no-secondary-label-chrome-linux.png new file mode 100644 index 00000000000..bf20c44e5a7 Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-default-secondary-metric-with-no-badge-and-no-secondary-label-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-titles-at-the-top-center-aligned-and-a-metric-icon-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-titles-at-the-top-center-aligned-and-a-metric-icon-chrome-linux.png new file mode 100644 index 00000000000..ac0c9401054 Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-titles-at-the-top-center-aligned-and-a-metric-icon-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-value-at-the-top-center-aligned-and-a-metric-icon-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-value-at-the-top-center-aligned-and-a-metric-icon-chrome-linux.png new file mode 100644 index 00000000000..f7723ae773c Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/layout-configuration/with-value-at-the-top-center-aligned-and-a-metric-icon-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/dark-theme/should-render-metric-with-click-interaction-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/dark-theme/should-render-metric-with-click-interaction-chrome-linux.png index 501014700e4..90657f35da7 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/dark-theme/should-render-metric-with-click-interaction-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/dark-theme/should-render-metric-with-click-interaction-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/dark-theme/should-render-metric-with-hover-interaction-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/dark-theme/should-render-metric-with-hover-interaction-chrome-linux.png index 4f5c39ea452..c0dcd1cef54 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/dark-theme/should-render-metric-with-hover-interaction-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/dark-theme/should-render-metric-with-hover-interaction-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/dark-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/dark-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png index 3928a6ae112..e10e7574276 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/dark-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/dark-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/light-theme/should-render-metric-with-click-interaction-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/light-theme/should-render-metric-with-click-interaction-chrome-linux.png index f68bc58a304..9c93bd15e79 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/light-theme/should-render-metric-with-click-interaction-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/light-theme/should-render-metric-with-click-interaction-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/light-theme/should-render-metric-with-hover-interaction-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/light-theme/should-render-metric-with-hover-interaction-chrome-linux.png index a95c93f1efb..522e6a41f1e 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/light-theme/should-render-metric-with-hover-interaction-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/light-theme/should-render-metric-with-hover-interaction-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/light-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/light-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png index b2e6344c107..cedde78f827 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/light-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/light-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-with-blended-background-color-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-with-blended-background-color-chrome-linux.png index ba628c131c1..7682bd4525c 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-with-blended-background-color-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-bar-type/should-render-with-blended-background-color-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/dark-theme/should-render-metric-with-click-interaction-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/dark-theme/should-render-metric-with-click-interaction-chrome-linux.png index 4d05403ca53..586087d797d 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/dark-theme/should-render-metric-with-click-interaction-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/dark-theme/should-render-metric-with-click-interaction-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/dark-theme/should-render-metric-with-hover-interaction-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/dark-theme/should-render-metric-with-hover-interaction-chrome-linux.png index 720f42c0d2a..b62a2137350 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/dark-theme/should-render-metric-with-hover-interaction-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/dark-theme/should-render-metric-with-hover-interaction-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/dark-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/dark-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png index 09f99d3a292..0927c6d9e08 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/dark-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/dark-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/light-theme/should-render-metric-with-click-interaction-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/light-theme/should-render-metric-with-click-interaction-chrome-linux.png index a1fb7634de6..e5c6bea04ae 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/light-theme/should-render-metric-with-click-interaction-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/light-theme/should-render-metric-with-click-interaction-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/light-theme/should-render-metric-with-hover-interaction-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/light-theme/should-render-metric-with-hover-interaction-chrome-linux.png index aa04dc2ec2e..d848dac3d4f 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/light-theme/should-render-metric-with-hover-interaction-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/light-theme/should-render-metric-with-hover-interaction-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/light-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/light-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png index 48cfbbaf448..9071d107ea3 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/light-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/light-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-with-blended-background-color-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-with-blended-background-color-chrome-linux.png index e98d49eac18..2e617bf609b 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-with-blended-background-color-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-none-type/should-render-with-blended-background-color-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/dark-theme/should-render-metric-with-click-interaction-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/dark-theme/should-render-metric-with-click-interaction-chrome-linux.png index ae5fc59e186..895c1c8f6ec 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/dark-theme/should-render-metric-with-click-interaction-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/dark-theme/should-render-metric-with-click-interaction-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/dark-theme/should-render-metric-with-hover-interaction-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/dark-theme/should-render-metric-with-hover-interaction-chrome-linux.png index 3637856e340..9283fdb8f37 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/dark-theme/should-render-metric-with-hover-interaction-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/dark-theme/should-render-metric-with-hover-interaction-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/dark-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/dark-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png index 64620cda558..1b5e0adbd55 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/dark-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/dark-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/light-theme/should-render-metric-with-click-interaction-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/light-theme/should-render-metric-with-click-interaction-chrome-linux.png index 0f4da284440..62cfde2898d 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/light-theme/should-render-metric-with-click-interaction-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/light-theme/should-render-metric-with-click-interaction-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/light-theme/should-render-metric-with-hover-interaction-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/light-theme/should-render-metric-with-hover-interaction-chrome-linux.png index 08ee5e80044..2765abfbafc 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/light-theme/should-render-metric-with-hover-interaction-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/light-theme/should-render-metric-with-hover-interaction-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/light-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/light-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png index ea0331b7b4d..eed2aceab5e 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/light-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/light-theme/should-render-metric-with-transparent-bg-color-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-with-blended-background-color-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-with-blended-background-color-chrome-linux.png index 33698e833e8..10843209ccd 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-with-blended-background-color-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/metric-trend-type/should-render-with-blended-background-color-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 047661a8dee..ab132f475ed 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 251cd3b8c81..deaa62297e9 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-metric-body-content-when-body-overflows-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-metric-body-content-when-body-overflows-chrome-linux.png new file mode 100644 index 00000000000..be7c51516a2 Binary files /dev/null and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-metric-body-content-when-body-overflows-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-metric-value-fit-font-size-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-metric-value-fit-font-size-chrome-linux.png index e33844fc2c1..4bf41ed4638 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-metric-value-fit-font-size-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-metric-value-fit-font-size-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 047661a8dee..ab132f475ed 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 251cd3b8c81..deaa62297e9 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 b434c3bd1a9..725c8e63182 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/should-render-with-empty-and-missing-background-colors-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-with-empty-and-missing-background-colors-chrome-linux.png index db5bcad6ee0..31f3f71082c 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-with-empty-and-missing-background-colors-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/should-render-with-empty-and-missing-background-colors-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/small-size-with-default-font-size-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/small-size-with-default-font-size-chrome-linux.png index 9085d8c0d4d..0be94fc2ede 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/small-size-with-default-font-size-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/small-size-with-default-font-size-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/small-size-with-fit-font-size-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/small-size-with-fit-font-size-chrome-linux.png index 182ec2119a0..7787c0f42f7 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/small-size-with-fit-font-size-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/small-size-with-fit-font-size-chrome-linux.png differ diff --git a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/small-size-with-fixed-font-size-chrome-linux.png b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/small-size-with-fixed-font-size-chrome-linux.png index ca3a34e455e..2051a935337 100644 Binary files a/e2e/screenshots/metric_stories.test.ts-snapshots/metric/small-size-with-fixed-font-size-chrome-linux.png and b/e2e/screenshots/metric_stories.test.ts-snapshots/metric/small-size-with-fixed-font-size-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 6ce82a70c50..92c9e5e2d69 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 88d823a14a7..79aefe4f926 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 e3b0737be07..9b0cd29e6ee 100644 --- a/e2e/tests/metric_stories.test.ts +++ b/e2e/tests/metric_stories.test.ts @@ -62,6 +62,16 @@ test.describe('Metric', () => { ); }); + test('should render metric body content when body overflows', async ({ page }) => { + await common.expectChartAtUrlToMatchScreenshot(page)( + 'http://localhost:9001/?path=/story/metric-alpha--body-content', + { + maxDiffPixels: 5, + action: async () => await common.setResizeDimensions(page)({ height: 100, width: 180 }), + }, + ); + }); + test('should render metric value fit font size', async ({ page }) => { await common.expectChartAtUrlToMatchScreenshot(page)( 'http://localhost:9001/?path=/story/metric-alpha--array-of-values&globals=toggles.showHeader:true;toggles.showChartTitle:false;toggles.showChartDescription:false;toggles.showChartBoundary:false;theme:light&knob-Blue groupId_Annotations=primary&knob-Dataset_Legend=shortCopyDataset&knob-Domain axis_Annotations=y&knob-EUI icon glyph name=warning&knob-EUI value icon glyph name=sortUp&knob-Enable debug state=true&knob-FadeOnFocusingOthers_Animations=true&knob-Hide color picker_Legend=true&knob-Legend Value_Legend=median,min,max&knob-Legend position_Legend=right&knob-Number formatting precision_Legend=2&knob-Outside dimension_Annotations=4&knob-Popover position_Legend=leftCenter&knob-Red groupId_Annotations=primary&knob-Render outside chart_Annotations=true&knob-Scale type=linear&knob-Series type=area&knob-SeriesType=bar&knob-Tick size=10&knob-annotation count_Styles=6&knob-annotation opacity_Styles=0.5&knob-annotation zIndex_Styles=0&knob-attach click handler=true&knob-blending background=rgba(255,255,255,1)&knob-chartRotation=180&knob-empty background=transparent&knob-max trend data points=30&knob-number of columns=3&knob-value font mode=fit&knob-value font size (px)=40&knob-show grid border=&knob-debug randomized data=', @@ -105,6 +115,62 @@ test.describe('Metric', () => { } }); + test.describe('Layout configuration', () => { + test(`with default secondary and 'before' secondary label position`, async ({ page }) => { + await common.expectChartAtUrlToMatchScreenshot(page)( + `http://localhost:9001/?path=/story/metric-alpha--layout&globals=toggles.showHeader:true;toggles.showChartTitle:false;toggles.showChartDescription:false;toggles.showChartBoundary:false;theme:light&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 5m&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=vertical&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=&knob-Subtitle=Little description of this component&knob-Title=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=55.23&knob-end_General=100&knob-format (numeraljs)_General=0.[0]&knob-start_General=9&knob-subtype_General=horizontal&knob-target_General=75&knob-tick strategy_Ticks=auto&knob-ticks(approx. count)_Ticks=5&knob-ticks(placements)_Ticks=-,2,0,,,5,,,1,0,,,1,5,,,0,2,0,,,2,5,,,5,0,,,1,0,0,0,,,2,0,0,,,-,1,0,0,,&knob-title_General=Error rate&knob-value_General=56&knob-Visualization type=bar&knob-Value prefix=&knob-Value postfix= %&knob-Use custom extra_Secondary metric=&knob-Use value color_Colors=&knob-Value color_Colors=#065B58&knob-Use blending background_Colors=&knob-Show value icon=`, + { + action: async () => await common.setResizeDimensions(page)({ height: 180, width: 180 }), + }, + ); + }); + + test(`with default secondary and 'after' secondary label position`, async ({ page }) => { + await common.expectChartAtUrlToMatchScreenshot(page)( + `http://localhost:9001/?path=/story/metric-alpha--layout&globals=toggles.showHeader:true;toggles.showChartTitle:false;toggles.showChartDescription:false;toggles.showChartBoundary:false;theme:light&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 5m&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=vertical&knob-Progress max=100&knob-Secondary metric label position_Secondary metric=after&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=&knob-Subtitle=Little description of this component&knob-Title=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=55.23&knob-end_General=100&knob-format (numeraljs)_General=0.[0]&knob-start_General=9&knob-subtype_General=horizontal&knob-target_General=75&knob-tick strategy_Ticks=auto&knob-ticks(approx. count)_Ticks=5&knob-ticks(placements)_Ticks=-,2,0,,,5,,,1,0,,,1,5,,,0,2,0,,,2,5,,,5,0,,,1,0,0,0,,,2,0,0,,,-,1,0,0,,&knob-title_General=Error rate&knob-value_General=56&knob-Visualization type=bar&knob-Value prefix=&knob-Value postfix= %&knob-Use custom extra_Secondary metric=&knob-Use value color_Colors=&knob-Value color_Colors=#065B58&knob-Use blending background_Colors=&knob-Show value icon=`, + { + action: async () => await common.setResizeDimensions(page)({ height: 180, width: 180 }), + }, + ); + }); + + test(`with default secondary metric with badge and no secondary label`, async ({ page }) => { + await common.expectChartAtUrlToMatchScreenshot(page)( + `http://localhost:9001/?path=/story/metric-alpha--layout&globals=toggles.showHeader:true;toggles.showChartTitle:false;toggles.showChartDescription:false;toggles.showChartBoundary:false;theme:light&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 5m&knob-Extra element alignment_Text configuration and position=left&knob-Is numeric metric=true&knob-Label_Secondary metric=&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=vertical&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=&knob-Subtitle=Little description of this component&knob-Title=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=55.23&knob-end_General=100&knob-format (numeraljs)_General=0.[0]&knob-start_General=9&knob-subtype_General=horizontal&knob-target_General=75&knob-tick strategy_Ticks=auto&knob-ticks(approx. count)_Ticks=5&knob-ticks(placements)_Ticks=-,2,0,,,5,,,1,0,,,1,5,,,0,2,0,,,2,5,,,5,0,,,1,0,0,0,,,2,0,0,,,-,1,0,0,,&knob-title_General=Error rate&knob-value_General=56&knob-Visualization type=bar&knob-Value prefix=&knob-Value postfix= %&knob-Use custom extra_Secondary metric=&knob-Use value color_Colors=&knob-Value color_Colors=#065B58&knob-Use blending background_Colors=&knob-Show value icon=`, + { + action: async () => await common.setResizeDimensions(page)({ height: 180, width: 180 }), + }, + ); + }); + + test(`with default secondary metric with no badge and no secondary label`, async ({ page }) => { + await common.expectChartAtUrlToMatchScreenshot(page)( + `http://localhost:9001/?path=/story/metric-alpha--layout&globals=toggles.showHeader:true;toggles.showChartTitle:false;toggles.showChartDescription:false;toggles.showChartBoundary:false;theme:light&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=&knob-Custom extra_Secondary metric=last 5m&knob-Extra element alignment_Text configuration and position=left&knob-Is numeric metric=true&knob-Label_Secondary metric=&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=vertical&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=&knob-Subtitle=Little description of this component&knob-Title=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=55.23&knob-end_General=100&knob-format (numeraljs)_General=0.[0]&knob-start_General=9&knob-subtype_General=horizontal&knob-target_General=75&knob-tick strategy_Ticks=auto&knob-ticks(approx. count)_Ticks=5&knob-ticks(placements)_Ticks=-,2,0,,,5,,,1,0,,,1,5,,,0,2,0,,,2,5,,,5,0,,,1,0,0,0,,,2,0,0,,,-,1,0,0,,&knob-title_General=Error rate&knob-value_General=56&knob-Visualization type=bar&knob-Value prefix=&knob-Value postfix= %&knob-Use custom extra_Secondary metric=&knob-Use value color_Colors=&knob-Value color_Colors=#065B58&knob-Use blending background_Colors=&knob-Show value icon=`, + { + action: async () => await common.setResizeDimensions(page)({ height: 180, width: 180 }), + }, + ); + }); + + test(`with value at the top, center aligned, and a metric icon`, async ({ page }) => { + await common.expectChartAtUrlToMatchScreenshot(page)( + `http://localhost:9001/?path=/story/metric-alpha--layout&globals=toggles.showHeader:true;toggles.showChartTitle:false;toggles.showChartDescription:false;toggles.showChartBoundary:false;theme:light&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 5m&knob-Extra element alignment_Text configuration and position=left&knob-Is numeric metric=true&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=center&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=vertical&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-Subtitle=Little description of this component&knob-Title=Count of records&knob-Title and subtitle alignment_Text configuration and position=center&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=55.23&knob-Value color_Colors=#065B58&knob-Visualization type=bar&knob-Value prefix=&knob-Value postfix= %&knob-Use custom extra_Secondary metric=&knob-Label_Secondary metric=Last week&knob-Use value color_Colors=&knob-Use blending background_Colors=&knob-Show value icon=`, + { + action: async () => await common.setResizeDimensions(page)({ height: 180, width: 180 }), + }, + ); + }); + + test(`with titles at the top, center aligned, and a metric icon`, async ({ page }) => { + await common.expectChartAtUrlToMatchScreenshot(page)( + `http://localhost:9001/?path=/story/metric-alpha--layout&globals=toggles.showHeader:true;toggles.showChartTitle:false;toggles.showChartDescription:false;toggles.showChartBoundary:false;theme:light&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 5m&knob-Extra element alignment_Text configuration and position=left&knob-Is numeric metric=true&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=center&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=bottom&knob-Progress bar direction=vertical&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-Subtitle=Little description of this component&knob-Title=Count of records&knob-Title and subtitle alignment_Text configuration and position=center&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=55.23&knob-Value color_Colors=#065B58&knob-Visualization type=bar&knob-Value prefix=&knob-Value postfix= %&knob-Use custom extra_Secondary metric=&knob-Label_Secondary metric=Last week&knob-Use value color_Colors=&knob-Use blending background_Colors=&knob-Show value icon=`, + { + action: async () => await common.setResizeDimensions(page)({ height: 180, width: 180 }), + }, + ); + }); + }); + pwEach.describe(['trend', 'bar', 'none'])( (v) => `Metric - ${v} type`, (type) => { diff --git a/packages/charts/api/charts.api.md b/packages/charts/api/charts.api.md index 0dbb9599f4b..b1bb671aa2d 100644 --- a/packages/charts/api/charts.api.md +++ b/packages/charts/api/charts.api.md @@ -2067,7 +2067,7 @@ export type MetricBase = { extra?: ReactElement | ComponentType<{ fontSize: number; color: string; - }>; + }> | SecondaryMetricProps; icon?: ComponentType<{ width: number; height: number; @@ -2086,6 +2086,11 @@ export type MetricElementEvent = { columnIndex: number; }; +// Warning: (ae-forgotten-export) The symbol "FontWeight" needs to be exported by the entry point index.d.ts +// +// @public +export type MetricFontWeight = Extract; + // @alpha (undocumented) export interface MetricSpec extends Spec { // (undocumented) @@ -2099,7 +2104,7 @@ export interface MetricSpec extends Spec { // @alpha @deprecated (undocumented) export type MetricSpecProps = ComponentProps; -// @public (undocumented) +// @public export interface MetricStyle { // (undocumented) barBackground: Color; @@ -2109,7 +2114,9 @@ export interface MetricStyle { // (undocumented) emptyBackground: Color; // (undocumented) - iconAlign: 'left' | 'right'; + extraTextAlign: Extract; + // (undocumented) + iconAlign: Extract; // (undocumented) minHeight: Pixels; // (undocumented) @@ -2119,13 +2126,27 @@ export interface MetricStyle { // (undocumented) textDarkColor: Color; // (undocumented) + textExtraDarkColor: Color; + // (undocumented) + textExtraLightColor: Color; + // (undocumented) textLightColor: Color; // (undocumented) - titlesTextAlign: 'left' | 'center' | 'right'; + textSubtitleDarkColor: Color; + // (undocumented) + textSubtitleLightColor: Color; + // Warning: (ae-forgotten-export) The symbol "TextAlign" needs to be exported by the entry point index.d.ts + // + // (undocumented) + titlesTextAlign: Extract; + // (undocumented) + titleWeight: MetricFontWeight; // (undocumented) valueFontSize: 'default' | 'fit' | number; // (undocumented) - valuesTextAlign: 'left' | 'center' | 'right'; + valuePosition: 'top' | 'bottom'; + // (undocumented) + valueTextAlign: Extract; } // @alpha (undocumented) @@ -2671,6 +2692,19 @@ export const ScaleType: Readonly<{ // @public export type ScaleType = $Values; +// @alpha +export interface SecondaryMetricProps { + ariaDescription?: string; + badgeBorderColor?: Color; + badgeColor?: Color; + icon?: string; + iconPosition?: 'before' | 'after'; + label?: string; + labelPosition?: 'before' | 'after'; + style?: CSSProperties; + value: string; +} + // @public (undocumented) export interface SectorGeomSpecY { // (undocumented) diff --git a/packages/charts/src/chart_types/metric/renderer/_index.scss b/packages/charts/src/chart_types/metric/renderer/_index.scss index 6c08dfb6fb3..ef58188ea6f 100644 --- a/packages/charts/src/chart_types/metric/renderer/_index.scss +++ b/packages/charts/src/chart_types/metric/renderer/_index.scss @@ -1,7 +1,9 @@ -$progress-bar-width: 10px; -$progress-bar-height: 10px; -$progress-bar-target-width: 4px; -$progress-bar-target-height: 2px; +@import './variables'; +@import 'dom/text'; +@import 'dom/progress'; +@import 'dom/sparkline'; +@import 'dom/secondary_metric'; +@import 'dom/badge'; .echMetricContainer { display: grid; @@ -49,16 +51,28 @@ $progress-bar-target-height: 2px; } &--vertical { - // TODO: find a better way to style based on sizes (i.e. sass functions) + // TODO: Find a better way to style based on sizes (i.e. sass functions) &.echMetric--withProgressBar { &--small { - padding-left: $progress-bar-width; + padding-left: $progress-bar-width-small + $padding; + } + &--medium { + padding-left: $progress-bar-width-medium + $padding; + } + &--large { + padding-left: $progress-bar-width-large + $padding; } } &.echMetric--withTargetProgressBar { &--small { - padding-left: $progress-bar-width + $progress-bar-target-width; + padding-left: $progress-bar-width-small + $padding + $progress-bar-target-width; + } + &--medium { + padding-left: $progress-bar-width-medium + $padding + $progress-bar-target-width; + } + &--large { + padding-left: $progress-bar-width-large + $padding + $progress-bar-target-width; } } } @@ -66,13 +80,25 @@ $progress-bar-target-height: 2px; &--horizontal { &.echMetric--withProgressBar { &--small { - padding-bottom: $progress-bar-height; + padding-bottom: $progress-bar-height-small + $padding; + } + &--medium { + padding-bottom: $progress-bar-height-medium + $padding; + } + &--large { + padding-bottom: $progress-bar-height-large + $padding; } } &.echMetric--withTargetProgressBar { &--small { - padding-bottom: $progress-bar-height + $progress-bar-target-height; + padding-bottom: $progress-bar-height-small + $padding + $progress-bar-target-height; + } + &--medium { + padding-bottom: $progress-bar-height-medium + $padding + $progress-bar-target-height; + } + &--large { + padding-bottom: $progress-bar-height-large + $padding + $progress-bar-target-height; } } } @@ -85,7 +111,3 @@ $progress-bar-target-height: 2px; width: 20px; border-bottom: 1px solid; } - -@import 'dom/text'; -@import 'dom/progress'; -@import 'dom/sparkline'; diff --git a/packages/charts/src/chart_types/metric/renderer/_variables.scss b/packages/charts/src/chart_types/metric/renderer/_variables.scss new file mode 100644 index 00000000000..e05c57061da --- /dev/null +++ b/packages/charts/src/chart_types/metric/renderer/_variables.scss @@ -0,0 +1,18 @@ +// Progress bar thickness by size +$progress-bar-thickness-small: 4px; +$progress-bar-thickness-medium: 8px; +$progress-bar-thickness-large: 16px; + +$progress-bar-width-small: $progress-bar-thickness-small; +$progress-bar-width-medium: $progress-bar-thickness-medium; +$progress-bar-width-large: $progress-bar-thickness-large; + +$progress-bar-height-small: $progress-bar-thickness-small; +$progress-bar-height-medium: $progress-bar-thickness-medium; +$progress-bar-height-large: $progress-bar-thickness-large; + +$padding: 8px; + +$progress-bar-target-size: 8px; +$progress-bar-target-width: $progress-bar-target-size; +$progress-bar-target-height: $progress-bar-target-size; diff --git a/packages/charts/src/chart_types/metric/renderer/dom/_badge.scss b/packages/charts/src/chart_types/metric/renderer/dom/_badge.scss new file mode 100644 index 00000000000..f70141d2d77 --- /dev/null +++ b/packages/charts/src/chart_types/metric/renderer/dom/_badge.scss @@ -0,0 +1,37 @@ +$echBadgeBackgroundColor: #e3e8f2; +$echBadgeTextColor: #07101f; + +.echBadge__content { + display: inline-flex; + align-items: center; + vertical-align: middle; + overflow: hidden; + + min-block-size: 18px; + flex-shrink: 0; + flex-grow: 0; + + padding-block: 0px; + padding-inline: 8px; + + border: 1px solid transparent; + border-radius: 2px; + + font-size: inherit; + line-height: inherit; + text-align: start; + text-decoration: none; + + background-color: $echBadgeBackgroundColor; + color: $echBadgeTextColor; + + cursor: inherit; +} + +.echBadge__text { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; + min-width: 0; +} diff --git a/packages/charts/src/chart_types/metric/renderer/dom/_progress.scss b/packages/charts/src/chart_types/metric/renderer/dom/_progress.scss index 7bc2809585c..a5d118356b0 100644 --- a/packages/charts/src/chart_types/metric/renderer/dom/_progress.scss +++ b/packages/charts/src/chart_types/metric/renderer/dom/_progress.scss @@ -1,37 +1,62 @@ +$progress-bar-border-radius: 8px; + .echSingleMetricProgress { position: absolute; + border-radius: $progress-bar-border-radius; &--vertical { - left: 0; + left: $padding; right: 0; - top: 0; + top: $padding; bottom: 0; - height: 100%; + height: calc(100% - #{$padding * 2}); width: 100%; &.echSingleMetricProgress--small { right: auto; - width: 10px; + width: $progress-bar-width-small; + } + + &.echSingleMetricProgress--medium { + right: auto; + width: $progress-bar-width-medium; + } + + &.echSingleMetricProgress--large { + right: auto; + width: $progress-bar-width-large; } } &--horizontal { - left: 0; + left: $padding; right: 0; - width: 100%; + width: calc(100% - #{$padding * 2}); top: 0; - bottom: 0; + bottom: $padding; height: 100%; &.echSingleMetricProgress--small { top: auto; - height: 10px; + height: $progress-bar-height-small; + } + + &.echSingleMetricProgress--medium { + top: auto; + height: $progress-bar-height-medium; + } + + &.echSingleMetricProgress--large { + top: auto; + height: $progress-bar-height-large; } } } .echSingleMetricProgressBar { transition: background-color ease-in-out 0.1s; + max-width: 100%; + max-height: 100%; &--vertical { position: absolute; @@ -75,17 +100,39 @@ justify-content: center; align-items: center; overflow: visible; + $line-width: 2px; $line-length: 13px; + $line-width-small: $line-width; + $line-length-small: $line-length / 2; + + $line-width-medium: $line-width; + $line-length-medium: $line-length; + + $line-width-large: $line-width * 2; + $line-length-large: $line-length * 2; + &--vertical { position: absolute; left: 0; &.echSingleMetricZeroBaseline--small { right: auto; - height: $line-width; - width: $line-length; + height: $line-width-small; + width: $line-length-small; + } + + &.echSingleMetricZeroBaseline--medium { + right: auto; + height: $line-width-medium; + width: $line-length-medium; + } + + &.echSingleMetricZeroBaseline--large { + right: auto; + height: $line-width-large; + width: $line-length-large; } } @@ -95,8 +142,20 @@ &.echSingleMetricZeroBaseline--small { top: auto; - width: $line-width; - height: $line-length; + width: $line-width-small; + height: $line-length-small; + } + + &.echSingleMetricZeroBaseline--medium { + top: auto; + width: $line-width-medium; + height: $line-length-medium; + } + + &.echSingleMetricZeroBaseline--large { + top: auto; + width: $line-width-large; + height: $line-length-large; } } } diff --git a/packages/charts/src/chart_types/metric/renderer/dom/_secondary_metric.scss b/packages/charts/src/chart_types/metric/renderer/dom/_secondary_metric.scss new file mode 100644 index 00000000000..b2b4a6a2b88 --- /dev/null +++ b/packages/charts/src/chart_types/metric/renderer/dom/_secondary_metric.scss @@ -0,0 +1,31 @@ +.echSecondaryMetric { + $element-spacing: 4px; + + display: flex; + gap: $element-spacing; + overflow: hidden; + align-items: center; + white-space: nowrap; + + &__label { + flex: 1 1 auto; + } + + &__value { + display: inline-flex; + flex: 1 0 auto; + max-width: calc(100% - $element-spacing); + + &--full { + flex: 1 1 auto; + max-width: 100%; // override max-width when there is no label + } + } + + &__truncate { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } +} diff --git a/packages/charts/src/chart_types/metric/renderer/dom/_text.scss b/packages/charts/src/chart_types/metric/renderer/dom/_text.scss index 461bc0e7f40..053c085808a 100644 --- a/packages/charts/src/chart_types/metric/renderer/dom/_text.scss +++ b/packages/charts/src/chart_types/metric/renderer/dom/_text.scss @@ -1,37 +1,32 @@ -$padding: 8px; - .echMetricText { position: relative; - padding: $padding; - height: 100%; z-index: 1; display: grid; - grid-template-columns: 100%; - grid-template-rows: min-content auto min-content; + grid-template-columns: minmax(0, 1fr); + grid-template-rows: min-content auto min-content min-content; + height: 100%; + padding: $padding; line-height: 1.2; - &__valuesBlock, + &__gap { + overflow: hidden; + } + &__titlesBlock { display: flex; flex-direction: column; + } + &__titlesBlock { &--left { align-items: start; text-align: left; - - .echMetricText__valueGroup { - justify-content: start; - } } &--center { align-items: center; text-align: center; - .echMetricText__valueGroup { - justify-content: center; - } - .echMetricText__title { button { width: 100%; @@ -42,48 +37,52 @@ $padding: 8px; &--right { align-items: end; text-align: right; - - .echMetricText__valueGroup { - justify-content: end; - } } } - &__valueGroup { - width: 100%; + .echMetricText__extraBlock { display: flex; - flex-direction: row; + align-items: center; + overflow: hidden; + + &--left { + justify-content: flex-start; + } + &--center { + justify-content: center; + } + &--right { + justify-content: flex-end; + } } - &__titleGroup { + &__valueBlock { + width: 100%; display: flex; flex-direction: row; + + &--left { + justify-content: start; + } + + &--center { + justify-content: center; + } + + &--right { + justify-content: end; + } } &__title { - font-weight: bold; word-wrap: break-word; width: 100%; button { - font-weight: bold; width: 95%; } } - &__icon { - position: absolute; - top: $padding; - - &--left { - left: $padding; - } - - &--right { - right: $padding; - } - } - &__subtitle { padding-top: 5px; font-weight: normal; @@ -109,16 +108,15 @@ $padding: 8px; font-weight: bold; } - &__gap { - position: relative; - } + &__icon { + display: flex; - &__body { - overflow: hidden; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; + &--left { + justify-content: left; + } + + &--right { + justify-content: right; + } } } diff --git a/packages/charts/src/chart_types/metric/renderer/dom/badge.tsx b/packages/charts/src/chart_types/metric/renderer/dom/badge.tsx new file mode 100644 index 00000000000..c52432a6898 --- /dev/null +++ b/packages/charts/src/chart_types/metric/renderer/dom/badge.tsx @@ -0,0 +1,62 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import classNames from 'classnames'; +import React from 'react'; + +import type { Color } from '../../../../common/colors'; +import { fillTextColor } from '../../../../common/fill_text_color'; + +/** @internal */ +export const BADGE_BORDER = 1; + +const ICON_MARGIN = 4; + +interface BadgeProps { + /** Additional CSS class name for the badge container */ + className?: string; + /** Text content to display in the badge */ + value: string; + /** Background color of the badge */ + backgroundColor: Color; + /** Optional border color (undefined = no border) */ + borderColor?: Color; + /** Optional icon to display with priority over text */ + icon?: string; + /** Icon position: 'before' or 'after' */ + iconPosition?: 'before' | 'after'; +} + +/** @internal */ +export const Badge: React.FC = ({ + className, + value, + backgroundColor, + borderColor, + icon, + iconPosition = 'after', +}) => { + const classes = classNames('echBadge__content', className); + const highContrastColor = fillTextColor(backgroundColor, backgroundColor); + + const iconStyles = + value !== '' + ? { + [iconPosition === 'before' ? 'marginInlineEnd' : 'marginInlineStart']: icon ? ICON_MARGIN : undefined, + } + : undefined; + const optionalIcon = icon ? {icon} : null; + + return ( + + {iconPosition === 'before' && optionalIcon} + {value} + {iconPosition === 'after' && optionalIcon} + + ); +}; 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 a14ab59e1ce..5412b856605 100644 --- a/packages/charts/src/chart_types/metric/renderer/dom/index.tsx +++ b/packages/charts/src/chart_types/metric/renderer/dom/index.tsx @@ -16,6 +16,7 @@ import type { Dispatch } from 'redux'; import { bindActionCreators } from 'redux'; import { Metric as MetricComponent } from './metric'; +import type { TextContrastOptions } from './metric'; import type { MetricTextDimensions } from './text_measurements'; import { getFittedFontSizes, @@ -24,7 +25,6 @@ import { getMetricTextPartDimensions, getSnappedFontSizes, } from './text_measurements'; -import type { ColorContrastOptions } from '../../../../common/color_calcs'; import { combineColors, highContrastColor } from '../../../../common/color_calcs'; import { colorToRgba, RGBATupleToString } from '../../../../common/color_library_wrappers'; import type { Color } from '../../../../common/colors'; @@ -67,6 +67,17 @@ interface DispatchProps { onChartRendered: typeof onChartRenderedAction; } +function getTextContrastOptions(style: MetricStyle): TextContrastOptions { + return { + text: { lightColor: colorToRgba(style.textLightColor), darkColor: colorToRgba(style.textDarkColor) }, + subtitle: { + lightColor: colorToRgba(style.textSubtitleLightColor), + darkColor: colorToRgba(style.textSubtitleDarkColor), + }, + extra: { lightColor: colorToRgba(style.textExtraLightColor), darkColor: colorToRgba(style.textExtraDarkColor) }, + }; +} + function Component({ chartId, hasTitles, @@ -96,14 +107,13 @@ function Component({ const maxColumns = data.reduce((acc, row) => Math.max(acc, row.length), 0); const panel = { width: width / maxColumns, height: height / totalRows }; - const contrastOptions: ColorContrastOptions = { - lightColor: colorToRgba(style.textLightColor), - darkColor: colorToRgba(style.textDarkColor), - }; + + const textContrastOptions = getTextContrastOptions(style); const emptyBackgroundRGBA = combineColors(colorToRgba(style.emptyBackground), colorToRgba(backgroundColor)); const emptyBackground = RGBATupleToString(emptyBackgroundRGBA); - const emptyForegroundColor = highContrastColor(emptyBackgroundRGBA, undefined, contrastOptions).color; + // using the text contrast options + const emptyForegroundColor = highContrastColor(emptyBackgroundRGBA, undefined, textContrastOptions.text).color; const metricsConfigs = data.reduce<{ fittedValueFontSize: number; @@ -136,16 +146,16 @@ function Component({ }; } const textDimensions = getMetricTextPartDimensions(datum, panel, style, locale); - - const fontSize = getFitValueFontSize( + const totalWidth = panel.width - textDimensions.progressBarWidth - textDimensions.iconGridColumnWidth; + const valueFontSize = getFitValueFontSize( textDimensions.heightBasedSizes.valueFontSize, - panel.width - textDimensions.progressBarWidth, + totalWidth, textDimensions.visibility.gapHeight, textDimensions.textParts, style.minValueFontSize, datum.valueIcon !== undefined, ); - acc.fittedValueFontSize = Math.min(acc.fittedValueFontSize, fontSize); + acc.fittedValueFontSize = Math.min(acc.fittedValueFontSize, valueFontSize); return { type: 'metric', @@ -223,7 +233,7 @@ function Component({ columnIndex={config.columnIndex} style={style} backgroundColor={backgroundColor} - contrastOptions={contrastOptions} + contrastOptions={textContrastOptions} onElementClick={onElementClick} onElementOut={onElementOut} onElementOver={onElementOver} 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 644d1f719e2..274810f2852 100644 --- a/packages/charts/src/chart_types/metric/renderer/dom/metric.tsx +++ b/packages/charts/src/chart_types/metric/renderer/dom/metric.tsx @@ -12,10 +12,11 @@ import React, { useState } from 'react'; import { ProgressBar } from './progress'; import { SparkLine, getSparkLineColor } from './sparkline'; +import type { TextColors } from './text'; import { MetricText } from './text'; import type { MetricTextDimensions } from './text_measurements'; import type { ColorContrastOptions } from '../../../../common/color_calcs'; -import { combineColors } from '../../../../common/color_calcs'; +import { combineColors, getContrastRecommendation } from '../../../../common/color_calcs'; import { RGBATupleToString, changeColorLightness, colorToRgba } from '../../../../common/color_library_wrappers'; import type { Color } from '../../../../common/colors'; import { DEFAULT_CSS_CURSOR } from '../../../../common/constants'; @@ -30,7 +31,94 @@ import type { import { LayoutDirection, isNil } from '../../../../utils/common'; import type { MetricStyle } from '../../../../utils/themes/theme'; import type { MetricWNumber } from '../../specs'; -import { isMetricWProgress, isMetricWTrend } from '../../specs'; +import { isMetricWProgress, isMetricWTrend, isSecondaryMetricProps } from '../../specs'; + +/** @internal */ +export interface TextContrastOptions { + text: ColorContrastOptions; + subtitle: ColorContrastOptions; + extra: ColorContrastOptions; +} + +/** + * Synced with _index.scss + * @internal + */ +export type ProgressBarSize = 'small' | 'medium' | 'large'; +const progressBarMap: Record = { + 4: 'small', + 8: 'medium', + 16: 'large', +}; + +interface MetricContext { + backgroundColor: Color; + blendedColor: Color; + hasProgressBar: boolean; + hasTrend: boolean; +} + +const getTextColor = ({ + metricContext: { backgroundColor, blendedColor, hasProgressBar, hasTrend }, + contrastOptions, +}: { + metricContext: MetricContext; + contrastOptions: ColorContrastOptions; +}) => { + const highContrastTextColor = fillTextColor( + backgroundColor, + hasProgressBar ? backgroundColor : blendedColor, + undefined, + contrastOptions, + ); + + let finalTextColor = highContrastTextColor.color; + + if (hasTrend) { + const { ratio, color, shade } = fillTextColor( + backgroundColor, + getSparkLineColor(blendedColor), + undefined, + contrastOptions, + ); + + // TODO verify this check is applied correctly + if (shade !== highContrastTextColor.shade && ratio > highContrastTextColor.ratio) { + finalTextColor = color; + } + } + + return finalTextColor.keyword; +}; + +const getTextColors = ({ + metricContext, + textContrastOptions, +}: { + metricContext: MetricContext; + textContrastOptions: TextContrastOptions; +}): TextColors => { + return { + highContrast: getTextColor({ + metricContext, + contrastOptions: textContrastOptions.text, + }), + subtitle: getTextColor({ + metricContext, + contrastOptions: textContrastOptions.subtitle, + }), + extra: getTextColor({ + metricContext, + contrastOptions: textContrastOptions.extra, + }), + }; +}; + +const CONTRAST_THRESHOLD = 3.0; + +function isColorContrastOptions(options: ColorContrastOptions | TextContrastOptions): options is ColorContrastOptions { + return !('text' in options); +} /** @internal */ export const Metric: React.FunctionComponent<{ @@ -43,7 +131,7 @@ export const Metric: React.FunctionComponent<{ datum: MetricDatum; style: MetricStyle; backgroundColor: Color; - contrastOptions: ColorContrastOptions; + contrastOptions: ColorContrastOptions | TextContrastOptions; textDimensions: MetricTextDimensions; onElementClick?: ElementClickListener; onElementOver?: ElementOverListener; @@ -64,13 +152,18 @@ export const Metric: React.FunctionComponent<{ onElementOver, onElementOut, }) => { - const progressBarSize = 'small'; // currently we provide only the small progress bar; + const { progressBarThickness } = textDimensions.heightBasedSizes; + const progressBarSize = progressBarMap[progressBarThickness] ?? 'medium'; + const [mouseState, setMouseState] = useState<'leave' | 'enter' | 'down'>('leave'); const [lastMouseDownTimestamp, setLastMouseDownTimestamp] = useState(0); const metricHTMLId = `echMetric-${chartId}-${rowIndex}-${columnIndex}`; + const hasProgressBar = isMetricWProgress(datum); const progressBarDirection = hasProgressBar ? datum.progressBarDirection : undefined; + const hasTrend = isMetricWTrend(datum); + const containerClassName = classNames('echMetric', { 'echMetric--rightBorder': columnIndex < totalColumns - 1, 'echMetric--bottomBorder': rowIndex < totalRows - 1, @@ -100,30 +193,45 @@ export const Metric: React.FunctionComponent<{ const event: MetricElementEvent = { type: 'metricElementEvent', rowIndex, columnIndex }; const containerStyle: CSSProperties = { - backgroundColor: isMetricWTrend(datumWithInteractionColor) ? backgroundColor : datumWithInteractionColor.color, + backgroundColor: hasTrend ? backgroundColor : datumWithInteractionColor.color, cursor: onElementClick ? 'pointer' : DEFAULT_CSS_CURSOR, borderColor: style.border, }; - const highContrastTextColor = fillTextColor( - backgroundColor, - isMetricWProgress(datum) ? backgroundColor : blendedColor, - undefined, - contrastOptions, - ); - let finalTextColor = highContrastTextColor.color; + const textContrastOptions = isColorContrastOptions(contrastOptions) + ? { + text: contrastOptions, + subtitle: contrastOptions, + extra: contrastOptions, + } + : contrastOptions; - if (isMetricWTrend(datum)) { - const { ratio, color, shade } = fillTextColor( - backgroundColor, - getSparkLineColor(blendedColor), - undefined, - contrastOptions, - ); + const textColors = getTextColors({ + metricContext: { backgroundColor, blendedColor, hasProgressBar, hasTrend }, + textContrastOptions, + }); - // TODO verify this check is applied correctly - if (shade !== highContrastTextColor.shade && ratio > highContrastTextColor.ratio) { - finalTextColor = color; + // Note: Added here the calculation because we have all the metric context and info + let badgeBorderColor; + if (isSecondaryMetricProps(datum.extra) && !!datum.extra.badgeColor && !datum.extra.badgeBorderColor) { + const metricBackgroundColor = hasProgressBar ? backgroundColor : blendedColor; + const borderRecommendation = getContrastRecommendation(metricBackgroundColor, datum.extra.badgeColor, { + contrastThreshold: CONTRAST_THRESHOLD, + borderOptions: textContrastOptions.extra, + }); + badgeBorderColor = borderRecommendation.borderColor; + if (hasTrend) { + const { shade, borderColor, contrastRatio } = getContrastRecommendation( + getSparkLineColor(blendedColor), + datum.extra.badgeColor, + { + contrastThreshold: CONTRAST_THRESHOLD, + borderOptions: textContrastOptions.extra, + }, + ); + if (shade !== borderRecommendation.shade && contrastRatio > borderRecommendation.contrastRatio) { + badgeBorderColor = borderColor; + } } } @@ -170,8 +278,9 @@ export const Metric: React.FunctionComponent<{ style={style} onElementClick={onElementClick ? onElementClickHandler : undefined} progressBarSize={progressBarSize} - highContrastTextColor={finalTextColor.keyword} textDimensions={textDimensions} + colors={textColors} + badgeBorderColor={badgeBorderColor} /> {isMetricWTrend(datumWithInteractionColor) && } {isMetricWProgress(datumWithInteractionColor) && ( @@ -182,7 +291,7 @@ export const Metric: React.FunctionComponent<{ size={progressBarSize} /> )} -
+
); }; diff --git a/packages/charts/src/chart_types/metric/renderer/dom/progress.tsx b/packages/charts/src/chart_types/metric/renderer/dom/progress.tsx index 4de19bea393..9f974622c42 100644 --- a/packages/charts/src/chart_types/metric/renderer/dom/progress.tsx +++ b/packages/charts/src/chart_types/metric/renderer/dom/progress.tsx @@ -8,8 +8,11 @@ import classNames from 'classnames'; import { scaleLinear } from 'd3-scale'; +import type { CSSProperties } from 'react'; import React from 'react'; +import type { ProgressBarSize } from './metric'; +import { PROGRESS_BAR_TARGET_SIZE } from './text_measurements'; import type { Color } from '../../../../common/colors'; import { Icon } from '../../../../components/icons/icon'; import { isNil, LayoutDirection, sortNumbers } from '../../../../utils/common'; @@ -17,14 +20,26 @@ import type { ContinuousDomain, GenericDomain } from '../../../../utils/domain'; import type { BulletMetricWProgress, MetricWProgress } from '../../specs'; import { isBulletMetric } from '../../specs'; -const TARGET_SIZE = 8; -const BASELINE_SIZE = 2; +/** + * Synced with _progress.scss + * @internal + */ +const PROGRESS_BAR_BORDER_RADIUS = 8; + +/** + * Synced with _progress.scss + */ +const zeroBaselineSizeMap = { + small: 2, + medium: 2, + large: 4, +}; interface ProgressBarProps { datum: MetricWProgress | BulletMetricWProgress; barBackground: Color; blendedBarColor: Color; - size: 'small'; + size: ProgressBarSize; } /** @internal */ @@ -49,24 +64,62 @@ export const ProgressBar: React.FunctionComponent = ({ const [domainMin, domainMax] = sortNumbers(updatedDomain) as ContinuousDomain; const scaledValue = scale(value); const [min, max] = sortNumbers([scale(0), scaledValue]); + const endValue = 100 - max; + const safeStartValue = Math.max(0, min); + const safeEndValue = Math.max(0, endValue); const positionStyle = isVertical ? { - bottom: `${min}%`, - top: `${100 - max}%`, + bottom: `${safeStartValue}%`, + top: `${safeEndValue}%`, } : { - left: `${min}%`, - right: `${100 - max}%`, + left: `${safeStartValue}%`, + right: `${safeEndValue}%`, }; - const targetPlacement = isNil(target) ? null : `calc(${scale(target)}% - ${TARGET_SIZE / 2}px)`; - const zeroPlacement = domainMin >= 0 || domainMax <= 0 ? null : `calc(${scale(0)}% - ${BASELINE_SIZE / 2}px)`; + let borderRadius: CSSProperties = {}; + if (!isVertical && endValue <= 0) { + borderRadius = { + ...borderRadius, + borderTopRightRadius: PROGRESS_BAR_BORDER_RADIUS, + borderBottomRightRadius: PROGRESS_BAR_BORDER_RADIUS, + }; + } + if (!isVertical && min <= 0) { + borderRadius = { + ...borderRadius, + borderTopLeftRadius: PROGRESS_BAR_BORDER_RADIUS, + borderBottomLeftRadius: PROGRESS_BAR_BORDER_RADIUS, + }; + } + + // For vertical progress bar + if (isVertical && endValue <= 0) { + borderRadius = { + ...borderRadius, + borderTopLeftRadius: PROGRESS_BAR_BORDER_RADIUS, + borderTopRightRadius: PROGRESS_BAR_BORDER_RADIUS, + }; + } + if (isVertical && min <= 0) { + borderRadius = { + ...borderRadius, + borderBottomLeftRadius: PROGRESS_BAR_BORDER_RADIUS, + borderBottomRightRadius: PROGRESS_BAR_BORDER_RADIUS, + }; + } + + const zeroBaselineSize = zeroBaselineSizeMap[size]; + + const targetPlacement = isNil(target) ? null : `calc(${scale(target)}% - ${PROGRESS_BAR_TARGET_SIZE / 2}px)`; + const zeroPlacement = domainMin >= 0 || domainMax <= 0 ? null : `calc(${scale(0)}% - ${zeroBaselineSize / 2}px)`; + const labelType = isBullet ? 'Value' : 'Percentage'; return (
{targetPlacement && ( @@ -80,7 +133,12 @@ export const ProgressBar: React.FunctionComponent = ({ target || 0, )}`} > - +
)} {zeroPlacement && ( @@ -94,7 +152,7 @@ export const ProgressBar: React.FunctionComponent = ({ )}
= ({ ); }; -function getDirectionalClasses(element: string, isVertical: boolean, size: ProgressBarProps['size']) { +function getDirectionalClasses(element: string, isVertical: boolean, progressBarSize: ProgressBarProps['size']) { const base = `echSingleMetric${element}`; - return classNames(base, `${base}--${size}`, { + return classNames(base, `${base}--${progressBarSize}`, { [`${base}--vertical`]: isVertical, [`${base}--horizontal`]: !isVertical, }); diff --git a/packages/charts/src/chart_types/metric/renderer/dom/secondary_metric.tsx b/packages/charts/src/chart_types/metric/renderer/dom/secondary_metric.tsx new file mode 100644 index 00000000000..c1ad218bc86 --- /dev/null +++ b/packages/charts/src/chart_types/metric/renderer/dom/secondary_metric.tsx @@ -0,0 +1,62 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import classNames from 'classnames'; +import React from 'react'; + +import { Badge } from './badge'; +import type { SecondaryMetricProps } from '../../specs'; + +/** @internal */ +export const SecondaryMetric: React.FC = ({ + value, + label, + badgeColor, + labelPosition = 'before', + style, + ariaDescription, + badgeBorderColor, + icon, + iconPosition, +}) => { + const hasLabel = !!label; + const labelNode = hasLabel ? ( + {label} + ) : undefined; + + return ( + + {labelPosition === 'before' && labelNode} + {badgeColor ? ( + + ) : ( + + {value} + + )} + {labelPosition === 'after' && labelNode} + + ); +}; diff --git a/packages/charts/src/chart_types/metric/renderer/dom/text.tsx b/packages/charts/src/chart_types/metric/renderer/dom/text.tsx index f2105e6ccfa..14ca79d48b3 100644 --- a/packages/charts/src/chart_types/metric/renderer/dom/text.tsx +++ b/packages/charts/src/chart_types/metric/renderer/dom/text.tsx @@ -7,39 +7,81 @@ */ import classNames from 'classnames'; -import type { CSSProperties } from 'react'; import React from 'react'; +import type { ProgressBarSize } from './metric'; +import { SecondaryMetric } from './secondary_metric'; import type { MetricTextDimensions } from './text_measurements'; import { PADDING } from './text_measurements'; +import { TitlesBlock } from './titles'; import type { Color } from '../../../../common/colors'; import { LayoutDirection, renderWithProps } from '../../../../utils/common'; import type { MetricStyle } from '../../../../utils/themes/theme'; import type { MetricDatum } from '../../specs'; -import { isMetricWNumber } from '../../specs'; - -function lineClamp(maxLines: number): CSSProperties { - return { - textOverflow: 'ellipsis', - display: '-webkit-box', - WebkitLineClamp: maxLines, // due to an issue with react CSSProperties filtering out this line, see https://github.com/facebook/react/issues/23033 - lineClamp: maxLines, - WebkitBoxOrient: 'vertical', - overflow: 'hidden', - whiteSpace: 'pre-wrap', - }; -} +import { isMetricWNumber, isSecondaryMetricProps } from '../../specs'; + +const GRID_SPAN_THREE = '1 / span 3'; + +const gridRows = { + top: { value: '1', titles: '2', body: '3', extra: '4' }, + bottom: { value: '4', titles: '1', body: '2', extra: '3' }, +}; + +const gridSingleColumn = { value: '1', titles: '1', body: '1', extra: '1' }; + +const gridColumnsValuePostitionTop = { + left: { value: '2 / span 2', titles: GRID_SPAN_THREE, body: GRID_SPAN_THREE, extra: GRID_SPAN_THREE }, + right: { value: '1 / span 2', titles: GRID_SPAN_THREE, body: GRID_SPAN_THREE, extra: GRID_SPAN_THREE }, +}; + +const gridColumnsValuePositionBottom = { + left: { value: GRID_SPAN_THREE, titles: '2 / span 2', body: GRID_SPAN_THREE, extra: GRID_SPAN_THREE }, + right: { value: GRID_SPAN_THREE, titles: '1 / span 2', body: GRID_SPAN_THREE, extra: GRID_SPAN_THREE }, +}; + +const gridColumns = { + top: gridColumnsValuePostitionTop, + bottom: gridColumnsValuePositionBottom, +}; + +// Allways use three columns when the icon is visible for symmetry and centering +const getGridTemplateColumnsWithIcon = (iconSize: number) => { + const iconSizeWithPadding = `${iconSize + PADDING}px`; + return `${iconSizeWithPadding} minmax(0, 1fr) ${iconSizeWithPadding}`; +}; /** @internal */ -export const MetricText: React.FunctionComponent<{ +export interface TextColors { + /** + * Default color, used for the title and the heading + */ + highContrast: Color; + subtitle: Color; + extra: Color; +} + +interface MetricTextprops { id: string; datum: MetricDatum; style: MetricStyle; onElementClick?: () => void; - highContrastTextColor: Color; - progressBarSize: 'small'; + progressBarSize: ProgressBarSize; textDimensions: MetricTextDimensions; -}> = ({ id, datum, style, onElementClick, highContrastTextColor, progressBarSize, textDimensions }) => { + colors: TextColors; + badgeBorderColor?: Color; +} + +/** @internal */ +export const MetricText: React.FC = ({ + id, + datum, + style, + onElementClick, + progressBarSize, + textDimensions, + colors, + badgeBorderColor, +}) => { const { heightBasedSizes: sizes, hasProgressBar, progressBarDirection, visibility, textParts } = textDimensions; const { extra, body } = datum; @@ -49,130 +91,132 @@ export const MetricText: React.FunctionComponent<{ 'echMetricText--horizontal': progressBarDirection === LayoutDirection.Horizontal, }); - const TitleElement = () => ( - - {datum.title} - - ); + const { valuePosition, iconAlign } = style; + const isIconVisible = !!datum.icon; + + const gridTemplateColumns = isIconVisible ? getGridTemplateColumnsWithIcon(sizes.iconSize) : undefined; + + const iconGridStyles = isIconVisible ? { gridRow: '1', gridColumn: iconAlign === 'left' ? '1' : '3' } : {}; + + const currentGridRows = gridRows[valuePosition]; + + let currentGridColumns = isIconVisible ? gridColumns[valuePosition][iconAlign] : gridSingleColumn; + if (isIconVisible && valuePosition === 'top' && style.valueTextAlign === 'center') { + currentGridColumns = { ...currentGridColumns, value: '2' }; // Center value in middle column + } + if (isIconVisible && valuePosition === 'bottom' && style.titlesTextAlign === 'center') { + currentGridColumns = { ...currentGridColumns, titles: '2' }; // Center titles in middle column + } + + let extraElement: React.JSX.Element | null = null; + if (isSecondaryMetricProps(extra)) { + const { style: extraStyle = {}, ...secondaryMetricProps } = extra; + + extraElement = ( + + ); + } else if (React.isValidElement(extra) || typeof extra === 'function') { + extraElement = ( +

+ {renderWithProps(extra, { fontSize: sizes.extraFontSize, color: colors.extra })} +

+ ); + } + return ( -
-
- {visibility.title && ( -

- {onElementClick ? ( - - ) : ( - - )} -

- )} - {visibility.subtitle && ( -

- {datum.subtitle} -

- )} -
+
+ {datum.icon && ( -
+
{renderWithProps(datum.icon, { width: sizes.iconSize, height: sizes.iconSize, - color: highContrastTextColor, + color: colors.highContrast, })}
)} -
{body &&
{body}
}
+
+ {body &&
{body}
} +
-
-
- {visibility.extra && ( -

- {renderWithProps(extra, { fontSize: sizes.extraFontSize, color: highContrastTextColor })} -

- )} -
+ {/* Extra Block */} +
+ {visibility.extra && extraElement} +
-
+ {/* Value Block */} +
+

text).join('')} + > + {textParts.map(({ emphasis, text }, i) => { + return emphasis === 'small' ? ( + + {text} + + ) : ( + text + ); + })} +

+ {datum.valueIcon && (

text).join('')} > - {textParts.map(({ emphasis, text }, i) => { - return emphasis === 'small' ? ( - - {text} - - ) : ( - text - ); + {renderWithProps(datum.valueIcon, { + width: sizes.valuePartFontSize, + height: sizes.valuePartFontSize, + color: datum.valueColor ?? colors.highContrast, + verticalAlign: 'middle', })}

- {datum.valueIcon && ( -

- {renderWithProps(datum.valueIcon, { - width: sizes.valuePartFontSize, - height: sizes.valuePartFontSize, - color: datum.valueColor ?? highContrastTextColor, - verticalAlign: 'middle', - })} -

- )} -
+ )}
); diff --git a/packages/charts/src/chart_types/metric/renderer/dom/text_measurements.tsx b/packages/charts/src/chart_types/metric/renderer/dom/text_measurements.tsx index 75605308bb6..ae91616e10f 100644 --- a/packages/charts/src/chart_types/metric/renderer/dom/text_measurements.tsx +++ b/packages/charts/src/chart_types/metric/renderer/dom/text_measurements.tsx @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { BADGE_BORDER } from './badge'; import type { TextParts } from './text_processing'; import { getTextParts } from './text_processing'; import { DEFAULT_FONT_FAMILY } from '../../../../common/default_theme_attributes'; @@ -19,13 +20,15 @@ import type { MetricStyle } from '../../../../utils/themes/theme'; import type { MetricDatum, MetricWNumber } from '../../specs'; import { isMetricWProgress } from '../../specs'; -interface HeightBasedSizes { +/** @internal */ +export interface HeightBasedSizes { iconSize: number; titleFontSize: number; subtitleFontSize: number; extraFontSize: number; valueFontSize: number; valuePartFontSize: number; + progressBarThickness: number; } type BreakPoint = 'xxxs' | 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl'; @@ -41,11 +44,16 @@ type ElementVisibility = { /** @internal */ export const PADDING = 8; -const PROGRESS_BAR_WIDTH = 10; // synced with scss variables -const PROGRESS_BAR_TARGET_WIDTH = 4; +/** + * Synced with _index.scss + * @internal + */ +export const PROGRESS_BAR_TARGET_SIZE = 8; const LINE_HEIGHT = 1.2; // aligned with our CSS const HEIGHT_BP: [number, number, BreakPoint][] = [ - [100, 200, 'xs'], + [0, 100, 'xxxs'], + [100, 150, 'xxs'], + [150, 200, 'xs'], [200, 300, 's'], [300, 400, 'm'], [400, 500, 'l'], @@ -97,6 +105,7 @@ const SUBTITLE_FONT: Font = { ...TITLE_FONT, fontWeight: 'normal', }; +const PROGRESS_BAR_THICKNESS: Record = { xxxs: 4, xxs: 4, xs: 8, s: 8, m: 8, l: 8, xl: 8, xxl: 16 }; /** * Approximate font size to fit given available space @@ -125,18 +134,30 @@ export function getFitValueFontSize( return Math.floor(Math.max(Math.min(heightConstrainedSize, widthConstrainedSize), minValueFontSize)); } +/** @internal */ +export interface Visibility extends ElementVisibility { + titleLines: string[]; + subtitleLines: string[]; + gapHeight: number; +} + /** @internal */ export interface MetricTextDimensions { heightBasedSizes: HeightBasedSizes; hasProgressBar: boolean; progressBarDirection: LayoutDirection | undefined; + /** + * This only applies when there is a progress bar and is vertical. + * We have added the padding into the calculation + */ progressBarWidth: number; - visibility: ElementVisibility & { - titleLines: string[]; - subtitleLines: string[]; - gapHeight: number; - }; + visibility: Visibility; textParts: TextParts[]; + /** + * This only applies when there is an icon and the value postition is top. + * We have added the padding into the calculation + */ + iconGridColumnWidth: number; } /** @internal */ @@ -146,21 +167,42 @@ export function getMetricTextPartDimensions( style: MetricStyle, locale: string, ): MetricTextDimensions { - const heightBasedSizes = getHeightBasedFontSizes(HEIGHT_BP, panel.height, style); + const heightBasedSizes = getHeightBasedFontSizes(HEIGHT_BP, Math.min(panel.height, panel.width), style); const hasProgressBar = isMetricWProgress(datum); const hasTarget = !isNil((datum as MetricWNumber)?.target); const progressBarDirection = isMetricWProgress(datum) ? datum.progressBarDirection : undefined; + const hasVerticalProgressBar = hasProgressBar && progressBarDirection === LayoutDirection.Vertical; + const hasHorizontalProgressBar = hasProgressBar && progressBarDirection === LayoutDirection.Horizontal; + + const { progressBarThickness, iconSize } = heightBasedSizes; + + const progressBarTotalSpace = progressBarThickness + (hasTarget ? PROGRESS_BAR_TARGET_SIZE : 0) + PADDING; + const progressBarWidth = hasVerticalProgressBar ? progressBarTotalSpace : 0; + const progressBarHeight = hasHorizontalProgressBar ? progressBarTotalSpace : 0; + + const isIconVisible = !!datum.icon && style.valuePosition === 'top'; + // The width of the icon column, including padding + const iconColumnWidth = iconSize + PADDING; + // If the value is center-aligned and the icon is visible, add an extra column width for visual centering + const needsCenterSpacer = isIconVisible && style.valueTextAlign === 'center'; + const iconGridColumnWidth = isIconVisible ? iconColumnWidth * (needsCenterSpacer ? 2 : 1) : 0; + return { heightBasedSizes, hasProgressBar, progressBarDirection, - progressBarWidth: - hasProgressBar && progressBarDirection === LayoutDirection.Vertical - ? PROGRESS_BAR_WIDTH + (hasTarget ? PROGRESS_BAR_TARGET_WIDTH : 0) - : 0, - visibility: elementVisibility(datum, panel, heightBasedSizes, locale, style.valueFontSize === 'fit'), + progressBarWidth, + visibility: elementVisibility( + datum, + panel, + heightBasedSizes, + locale, + style.valueFontSize === 'fit', + progressBarHeight, + ), textParts: getTextParts(datum, style), + iconGridColumnWidth, }; } @@ -182,6 +224,7 @@ function getHeightBasedFontSizes( extraFontSize: EXTRA_FONT_SIZE[size], valueFontSize, valuePartFontSize, + progressBarThickness: PROGRESS_BAR_THICKNESS[size], }; } @@ -223,13 +266,23 @@ export function getSnappedFontSizes( }; } -/** @internal */ -export function elementVisibility( +const getResponsiveBreakpoints = (title: boolean, subtitle: boolean, extra: boolean): Array => [ + { titleMaxLines: 3, subtitleMaxLines: 2, title, subtitle, extra }, + { titleMaxLines: 3, subtitleMaxLines: 1, title, subtitle, extra }, + { titleMaxLines: 2, subtitleMaxLines: 1, title, subtitle, extra }, + { titleMaxLines: 1, subtitleMaxLines: 1, title, subtitle, extra }, + { titleMaxLines: 1, subtitleMaxLines: 0, title, subtitle: false, extra }, + { titleMaxLines: 1, subtitleMaxLines: 0, title, subtitle: false, extra: false }, + { titleMaxLines: 1, subtitleMaxLines: 0, title, subtitle: false, extra: false }, +]; + +function elementVisibility( datum: MetricDatum, panel: Size, sizes: HeightBasedSizes, locale: string, fit: boolean, + progressBarHeight: number, ): ElementVisibility & { titleLines: string[]; subtitleLines: string[]; @@ -255,18 +308,16 @@ export function elementVisibility( : 0; }; - const extraHeight = sizes.extraFontSize * LINE_HEIGHT; + // If there is a badge, we add the padding to the extra height + const hasBadge = !!(datum?.extra && 'badgeColor' in datum?.extra && datum?.extra?.badgeColor); + const badgeHeight = hasBadge ? BADGE_BORDER * 2 : 0; + + // We assume that the extra element is taking one line + const extraHeight = sizes.extraFontSize * LINE_HEIGHT + badgeHeight; + const valueHeight = sizes.valueFontSize * LINE_HEIGHT; - const responsiveBreakPoints: Array = [ - { titleMaxLines: 3, subtitleMaxLines: 2, title: !!datum.title, subtitle: !!datum.subtitle, extra: !!datum.extra }, - { titleMaxLines: 3, subtitleMaxLines: 1, title: !!datum.title, subtitle: !!datum.subtitle, extra: !!datum.extra }, - { titleMaxLines: 2, subtitleMaxLines: 1, title: !!datum.title, subtitle: !!datum.subtitle, extra: !!datum.extra }, - { titleMaxLines: 1, subtitleMaxLines: 1, title: !!datum.title, subtitle: !!datum.subtitle, extra: !!datum.extra }, - { titleMaxLines: 1, subtitleMaxLines: 0, title: !!datum.title, subtitle: false, extra: !!datum.extra }, - { titleMaxLines: 1, subtitleMaxLines: 0, title: !!datum.title, subtitle: false, extra: false }, - { titleMaxLines: 1, subtitleMaxLines: 0, title: !!datum.title, subtitle: false, extra: false }, - ]; + const responsiveBreakPoints = getResponsiveBreakpoints(!!datum.title, !!datum.subtitle, !!datum.extra); const getCombinedHeight = ( { titleMaxLines, subtitleMaxLines, title, subtitle, extra }: ElementVisibility, @@ -276,14 +327,25 @@ export function elementVisibility( (subtitle && subtitleMaxLines > 0 ? subtitleHeight(subtitleMaxLines, measure) : 0) + (extra ? extraHeight : 0) + valueHeight + - PADDING; + PADDING + + // For the height calculation, take into account when there is an horizontal progress bar + progressBarHeight; + /** + * Determines if the given breakpoint should be considered "visible" + * for the provided text measurement. + */ const isVisible = (ev: ElementVisibility, measure: TextMeasure) => getCombinedHeight(ev, measure) < panel.height; return withTextMeasure((textMeasure) => { - const visibilityBreakpoint = fit - ? responsiveBreakPoints.at(0)! - : responsiveBreakPoints.find((breakpoint) => isVisible(breakpoint, textMeasure)) ?? responsiveBreakPoints.at(-1)!; + let visibilityBreakpoint: ElementVisibility; + + if (fit) { + visibilityBreakpoint = responsiveBreakPoints.at(0)!; + } else { + const found = responsiveBreakPoints.find((breakpoint) => isVisible(breakpoint, textMeasure)); + visibilityBreakpoint = found ?? responsiveBreakPoints.at(-1)!; + } return { ...visibilityBreakpoint, diff --git a/packages/charts/src/chart_types/metric/renderer/dom/titles.tsx b/packages/charts/src/chart_types/metric/renderer/dom/titles.tsx new file mode 100644 index 00000000000..6a5394eab55 --- /dev/null +++ b/packages/charts/src/chart_types/metric/renderer/dom/titles.tsx @@ -0,0 +1,179 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import classNames from 'classnames'; +import type { CSSProperties } from 'react'; +import React, { useMemo } from 'react'; + +import type { HeightBasedSizes, Visibility } from './text_measurements'; +import type { Color } from '../../../../common/colors'; +import type { MetricFontWeight, MetricStyle } from '../../../../utils/themes/theme'; + +function lineClamp(maxLines: number): CSSProperties { + return { + textOverflow: 'ellipsis', + display: '-webkit-box', + WebkitLineClamp: maxLines, // due to an issue with react CSSProperties filtering out this line, see https://github.com/facebook/react/issues/23033 + lineClamp: maxLines, + WebkitBoxOrient: 'vertical', + overflow: 'hidden', + whiteSpace: 'pre-wrap', + }; +} + +interface TitleProps { + metricId: string; + title: string | undefined; + titleWeight: MetricFontWeight; + fontSize: number; + textAlign: MetricStyle['titlesTextAlign']; + linesLength: number; + onElementClick?: () => void; +} + +const Title: React.FC = ({ + metricId, + title, + titleWeight, + fontSize, + textAlign, + linesLength, + onElementClick, +}) => { + const contentStyle = { + fontSize, + textAlign, + fontWeight: titleWeight, + ...lineClamp(linesLength), + }; + + const content = ( + + {title} + + ); + + return ( +

+ {onElementClick ? ( + + ) : ( + content + )} +

+ ); +}; + +interface SubtitleProps { + subtitle: string | undefined; + fontSize: number; + subtitleLines: string[]; + color: Color; +} + +const Subtitle: React.FC = ({ subtitle, fontSize, subtitleLines, color }) => { + return ( +

+ {subtitle} +

+ ); +}; + +interface TitlesBlockProps { + // Identification & text + metricId: string; + title: string | undefined; + subtitle: string | undefined; + + // Layout & sizing + sizes: HeightBasedSizes; + visibility: Visibility; + + // Alignment & icon + textAlign: MetricStyle['titlesTextAlign']; + titleWeight: MetricFontWeight; + isIconVisible: boolean; + + titlesRow: string; + titlesColumn: string; + + subtitleColor: Color; + + // Events + onElementClick?: () => void; +} + +/** @internal */ +export const TitlesBlock: React.FC = ({ + metricId, + title, + subtitle, + sizes, + visibility, + textAlign, + titleWeight, + isIconVisible, + titlesRow, + titlesColumn, + subtitleColor, + onElementClick, +}) => { + const { titleFontSize, subtitleFontSize } = sizes; + const { title: showTitle, subtitle: showSubtitle, titleLines, subtitleLines } = visibility; + + const titlesBlockStyle = useMemo(() => { + if (!isIconVisible) return undefined; + return { + gridRow: titlesRow, + gridColumn: titlesColumn, + }; + }, [isIconVisible, titlesRow, titlesColumn]); + + return ( +
+ {showTitle && ( + + )} + + {showSubtitle && ( + <Subtitle subtitle={subtitle} fontSize={subtitleFontSize} subtitleLines={subtitleLines} color={subtitleColor} /> + )} + </div> + ); +}; diff --git a/packages/charts/src/chart_types/metric/specs/index.ts b/packages/charts/src/chart_types/metric/specs/index.ts index d0e724f74e8..f17b52da1d3 100644 --- a/packages/charts/src/chart_types/metric/specs/index.ts +++ b/packages/charts/src/chart_types/metric/specs/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { ComponentProps, ComponentType, ReactElement, ReactNode } from 'react'; +import type { ComponentProps, ComponentType, CSSProperties, ReactElement, ReactNode } from 'react'; import type { $Values } from 'utility-types'; import { ChartType } from '../..'; @@ -18,6 +18,32 @@ import type { LayoutDirection, ValueFormatter } from '../../../utils/common'; import type { GenericDomain } from '../../../utils/domain'; import type { BulletValueLabels } from '../../bullet_graph/spec'; +/** + * Props for displaying a secondary metric value with optional label and badge styling. + * + * @alpha + * */ +export interface SecondaryMetricProps { + /** The main value to display */ + value: string; + /** Optional label to display alongside the value */ + label?: string; + /** Optional background color for the value badge. If not provided, no badge is displayed */ + badgeColor?: Color; + /** Determines whether the label appears before or after the value */ + labelPosition?: 'before' | 'after'; + /** Optional CSS properties to apply to the container element */ + style?: CSSProperties; + /** Optional aria description */ + ariaDescription?: string; + /** Border color applied to the badge */ + badgeBorderColor?: Color; + /** Optional icon displayed next to the text */ + icon?: string; + /** Determines whether the icon appears before or after the value */ + iconPosition?: 'before' | 'after'; +} + /** @alpha */ export type MetricBase = { color: Color; @@ -29,7 +55,12 @@ export type MetricBase = { valueColor?: Color; valueIcon?: ComponentType<{ width: number; height: number; color: Color; verticalAlign: 'middle' }>; subtitle?: string; - extra?: ReactElement | ComponentType<{ fontSize: number; color: string }>; + /** + * Optional extra content to display below the subtitle. + * - Can be a React element, a component type (which will receive `fontSize` and `color` props), or a SecondaryMetricProps object. + * - If a SecondaryMetricProps object is provided, it will be rendered using the SecondaryMetric component. + */ + extra?: ReactElement | ComponentType<{ fontSize: number; color: string }> | SecondaryMetricProps; icon?: ComponentType<{ width: number; height: number; color: Color }>; body?: ReactNode; }; @@ -179,3 +210,14 @@ export function isMetricWTrend(datum: MetricDatum): datum is MetricWTrend { !datum.hasOwnProperty('domainMax') ); } + +/** @internal */ +export const isSecondaryMetricProps = (props: any): props is SecondaryMetricProps => { + return ( + props && + 'value' in props && + typeof props.value === 'string' && + (props.label === undefined || typeof props.label === 'string') && + (props.badgeColor === undefined || typeof props.badgeColor === 'string') + ); +}; diff --git a/packages/charts/src/chart_types/specs.ts b/packages/charts/src/chart_types/specs.ts index 4565144a2c0..17d5cd54d79 100644 --- a/packages/charts/src/chart_types/specs.ts +++ b/packages/charts/src/chart_types/specs.ts @@ -48,6 +48,7 @@ export { MetricWTrend, MetricTrendShape, MetricDatum, + SecondaryMetricProps, } from './metric/specs'; export { Bullet, BulletProps, BulletSpec, BulletDatum, BulletSubtype, BulletValueLabels } from './bullet_graph/spec'; diff --git a/packages/charts/src/common/color_calcs.ts b/packages/charts/src/common/color_calcs.ts index 0a9cb9fa838..8ced4809f35 100644 --- a/packages/charts/src/common/color_calcs.ts +++ b/packages/charts/src/common/color_calcs.ts @@ -10,7 +10,7 @@ import type { Required } from 'utility-types'; import { APCAContrast } from './apca_color_contrast'; import type { RgbaTuple, RgbTuple } from './color_library_wrappers'; -import { RGBATupleToString } from './color_library_wrappers'; +import { colorToRgba, RGBATupleToString } from './color_library_wrappers'; import type { ColorDefinition } from './colors'; import { Colors } from './colors'; import { getWCAG2ContrastRatio } from './wcag2_color_contrast'; @@ -131,3 +131,76 @@ export function highContrastColor( ): HighContrastResult { return HIGH_CONTRAST_FN[mode](background, options); } + +/** + * Computes the contrast ratio between two colors and determines if a border is needed for sufficient visual separation. + * If the contrast is below the specified threshold, recommends a border color that has high contrast with both input colors. + * + * @param colorA - The first color to compare (can be background, foreground, or any color). + * @param colorB - The second color to compare. + * @param options - Optional settings for contrast mode, threshold, and border color preferences. + * + * @internal + */ +export function getContrastRecommendation( + colorA: string, + colorB: string, + options: { + contrastMode?: 'WCAG2' | 'WCAG3'; + contrastThreshold?: number; + borderOptions?: ColorContrastOptions; + } = {}, +) { + const { + contrastMode = 'WCAG2', + contrastThreshold = 4.5, // WCAG AA standard + borderOptions, + } = options; + + const rgbA = colorToRgba(colorA).slice(0, 3) as RgbTuple; + const rgbB = colorToRgba(colorB).slice(0, 3) as RgbTuple; + + const contrastRatio = + contrastMode === 'WCAG2' ? getWCAG2ContrastRatio(rgbA, rgbB) : Math.abs(APCAContrast(rgbA, rgbB)); + + // If contrast is sufficient, no border is needed + if (contrastRatio >= contrastThreshold) { + return { + needsBorder: false, + contrastRatio, + borderColor: undefined, + shade: undefined, + color1Contrast: undefined, + color2Contrast: undefined, + }; + } + + // Get the optimal border color (high contrast against both colors) + const color1Result = highContrastColor(rgbA, contrastMode, borderOptions); + const color2Result = highContrastColor(rgbB, contrastMode, borderOptions); + + const selectedShade = + color1Result.shade === color2Result.shade + ? color1Result.shade // If both prefer the same shade, use it + : color1Result.ratio > color2Result.ratio // Otherwise use the one with better overall contrast + ? color1Result.shade + : color2Result.shade; + + // Use the color that works best with both backgrounds + // This prefers borders that have sufficient contrast with both colors + const borderColor = + color1Result.shade === color2Result.shade + ? color1Result.color.keyword // If both prefer the same shade, use it + : color1Result.ratio > color2Result.ratio // Otherwise use the one with better overall contrast + ? color1Result.color.keyword + : color2Result.color.keyword; + + return { + needsBorder: true, + contrastRatio, + borderColor, + shade: selectedShade, + color1Contrast: color1Result.ratio, + color2Contrast: color2Result.ratio, + }; +} diff --git a/packages/charts/src/common/text_utils.ts b/packages/charts/src/common/text_utils.ts index a1bd046fa7a..98f8904163f 100644 --- a/packages/charts/src/common/text_utils.ts +++ b/packages/charts/src/common/text_utils.ts @@ -15,8 +15,8 @@ import { integerSnap, monotonicHillClimb } from '../solvers/monotonic_hill_climb import type { TextMeasure } from '../utils/bbox/canvas_text_bbox_calculator'; import type { Datum } from '../utils/common'; -const FONT_WEIGHTS_NUMERIC = [100, 200, 300, 400, 500, 600, 700, 800, 900]; -const FONT_WEIGHTS_ALPHA = ['normal', 'bold', 'lighter', 'bolder', 'inherit', 'initial', 'unset']; +const FONT_WEIGHTS_NUMERIC = [100, 200, 300, 400, 500, 600, 700, 800, 900] as const; +const FONT_WEIGHTS_ALPHA = ['normal', 'bold', 'lighter', 'bolder', 'inherit', 'initial', 'unset'] as const; /** * todo consider doing tighter control for permissible font families, eg. as in Kibana Canvas - expression language @@ -25,7 +25,7 @@ const FONT_WEIGHTS_ALPHA = ['normal', 'bold', 'lighter', 'bolder', 'inherit', 'i */ export type FontFamily = string; /** @public */ -export const FONT_WEIGHTS = Object.freeze([...FONT_WEIGHTS_NUMERIC, ...FONT_WEIGHTS_ALPHA] as const); +export const FONT_WEIGHTS = Object.freeze([...FONT_WEIGHTS_NUMERIC, ...FONT_WEIGHTS_ALPHA]); /** @public */ export const FONT_VARIANTS = Object.freeze(['normal', 'small-caps'] as const); /** @public */ diff --git a/packages/charts/src/utils/themes/amsterdam_dark_theme.ts b/packages/charts/src/utils/themes/amsterdam_dark_theme.ts index 7228f7f35eb..afa429a3172 100644 --- a/packages/charts/src/utils/themes/amsterdam_dark_theme.ts +++ b/packages/charts/src/utils/themes/amsterdam_dark_theme.ts @@ -418,16 +418,23 @@ export const AMSTERDAM_DARK_THEME: Theme = { metric: { textLightColor: '#E0E5EE', textDarkColor: '#343741', + textSubtitleLightColor: '#E0E5EE', + textSubtitleDarkColor: '#343741', + textExtraLightColor: '#E0E5EE', + textExtraDarkColor: '#343741', valueFontSize: 'default', minValueFontSize: 12, titlesTextAlign: 'left', - valuesTextAlign: 'right', + extraTextAlign: 'right', + valueTextAlign: 'right', + valuePosition: 'bottom', iconAlign: 'right', border: '#343741', barBackground: '#343741', emptyBackground: Colors.Transparent.keyword, nonFiniteText: 'N/A', minHeight: 64, + titleWeight: 'bold', }, bulletGraph: { textColor: '#E0E5EE', diff --git a/packages/charts/src/utils/themes/amsterdam_light_theme.ts b/packages/charts/src/utils/themes/amsterdam_light_theme.ts index 6ab395e938c..75622ab964c 100644 --- a/packages/charts/src/utils/themes/amsterdam_light_theme.ts +++ b/packages/charts/src/utils/themes/amsterdam_light_theme.ts @@ -418,16 +418,23 @@ export const AMSTERDAM_LIGHT_THEME: Theme = { metric: { textLightColor: '#E0E5EE', textDarkColor: '#343741', + textSubtitleLightColor: '#E0E5EE', + textSubtitleDarkColor: '#343741', + textExtraLightColor: '#E0E5EE', + textExtraDarkColor: '#343741', valueFontSize: 'default', minValueFontSize: 12, titlesTextAlign: 'left', - valuesTextAlign: 'right', + extraTextAlign: 'right', + valueTextAlign: 'right', + valuePosition: 'bottom', iconAlign: 'right', border: '#EDF0F5', barBackground: '#EDF0F5', emptyBackground: Colors.Transparent.keyword, nonFiniteText: 'N/A', minHeight: 64, + titleWeight: 'bold', }, bulletGraph: { textColor: '#343741', diff --git a/packages/charts/src/utils/themes/dark_theme.ts b/packages/charts/src/utils/themes/dark_theme.ts index 3c7a7b5b8f6..ea7cff99edf 100644 --- a/packages/charts/src/utils/themes/dark_theme.ts +++ b/packages/charts/src/utils/themes/dark_theme.ts @@ -439,17 +439,24 @@ export const DARK_THEME: Theme = { }, metric: { textLightColor: DARK_TEXT_COLORS.textHeading, + textSubtitleLightColor: DARK_TEXT_COLORS.textParagraph, + textExtraLightColor: DARK_TEXT_COLORS.textParagraph, textDarkColor: LIGHT_TEXT_COLORS.textHeading, + textSubtitleDarkColor: LIGHT_TEXT_COLORS.textParagraph, + textExtraDarkColor: LIGHT_TEXT_COLORS.textParagraph, valueFontSize: 'default', minValueFontSize: 12, titlesTextAlign: 'left', - valuesTextAlign: 'right', + extraTextAlign: 'right', + valueTextAlign: 'right', + valuePosition: 'bottom', iconAlign: 'right', border: DARK_BORDER_COLORS.borderBaseSubdued, barBackground: DARK_BACKGROUND_COLORS.backgroundBaseDisabled, emptyBackground: Colors.Transparent.keyword, nonFiniteText: 'N/A', minHeight: 64, + titleWeight: 'bold', }, bulletGraph: DARK_THEME_BULLET_STYLE, tooltip: { diff --git a/packages/charts/src/utils/themes/legacy_dark_theme.ts b/packages/charts/src/utils/themes/legacy_dark_theme.ts index 45c4e85374c..f0fb30a0911 100644 --- a/packages/charts/src/utils/themes/legacy_dark_theme.ts +++ b/packages/charts/src/utils/themes/legacy_dark_theme.ts @@ -422,16 +422,23 @@ export const LEGACY_DARK_THEME: Theme = { metric: { textLightColor: '#E0E5EE', textDarkColor: '#343741', + textSubtitleLightColor: '#E0E5EE', + textSubtitleDarkColor: '#343741', + textExtraLightColor: '#E0E5EE', + textExtraDarkColor: '#343741', valueFontSize: 'default', minValueFontSize: 12, titlesTextAlign: 'left', - valuesTextAlign: 'right', + extraTextAlign: 'right', + valueTextAlign: 'right', + valuePosition: 'bottom', iconAlign: 'right', border: '#343741', barBackground: '#343741', emptyBackground: Colors.Transparent.keyword, nonFiniteText: 'N/A', minHeight: 64, + titleWeight: 'bold', }, bulletGraph: DARK_THEME_BULLET_STYLE, tooltip: { diff --git a/packages/charts/src/utils/themes/legacy_light_theme.ts b/packages/charts/src/utils/themes/legacy_light_theme.ts index 214f96a88d2..396c205ccd9 100644 --- a/packages/charts/src/utils/themes/legacy_light_theme.ts +++ b/packages/charts/src/utils/themes/legacy_light_theme.ts @@ -420,17 +420,24 @@ export const LEGACY_LIGHT_THEME: Theme = { }, metric: { textLightColor: '#E0E5EE', + textSubtitleLightColor: '#E0E5EE', + textSubtitleDarkColor: '#343741', + textExtraLightColor: '#E0E5EE', + textExtraDarkColor: '#343741', textDarkColor: '#343741', valueFontSize: 'default', minValueFontSize: 12, titlesTextAlign: 'left', - valuesTextAlign: 'right', + extraTextAlign: 'right', + valueTextAlign: 'right', + valuePosition: 'bottom', iconAlign: 'right', border: '#EDF0F5', barBackground: '#EDF0F5', emptyBackground: Colors.Transparent.keyword, nonFiniteText: 'N/A', minHeight: 64, + titleWeight: 'bold', }, bulletGraph: LIGHT_THEME_BULLET_STYLE, tooltip: { diff --git a/packages/charts/src/utils/themes/light_theme.ts b/packages/charts/src/utils/themes/light_theme.ts index f15574dcc6d..f55d106687b 100644 --- a/packages/charts/src/utils/themes/light_theme.ts +++ b/packages/charts/src/utils/themes/light_theme.ts @@ -437,17 +437,24 @@ export const LIGHT_THEME: Theme = { }, metric: { textLightColor: DARK_TEXT_COLORS.textHeading, + textSubtitleLightColor: DARK_TEXT_COLORS.textParagraph, + textExtraLightColor: DARK_TEXT_COLORS.textParagraph, textDarkColor: LIGHT_TEXT_COLORS.textHeading, + textSubtitleDarkColor: LIGHT_TEXT_COLORS.textParagraph, + textExtraDarkColor: LIGHT_TEXT_COLORS.textParagraph, valueFontSize: 'default', minValueFontSize: 12, titlesTextAlign: 'left', - valuesTextAlign: 'right', + extraTextAlign: 'right', + valueTextAlign: 'right', + valuePosition: 'bottom', iconAlign: 'right', border: LIGHT_BORDER_COLORS.borderBaseSubdued, barBackground: LIGHT_BACKGROUND_COLORS.backgroundBaseDisabled, emptyBackground: Colors.Transparent.keyword, nonFiniteText: 'N/A', minHeight: 64, + titleWeight: 'bold', }, bulletGraph: LIGHT_THEME_BULLET_STYLE, tooltip: { diff --git a/packages/charts/src/utils/themes/theme.ts b/packages/charts/src/utils/themes/theme.ts index 63244cb7492..86a2040a3d9 100644 --- a/packages/charts/src/utils/themes/theme.ts +++ b/packages/charts/src/utils/themes/theme.ts @@ -13,7 +13,7 @@ import type { PartitionStyle } from './partition'; import type { BulletStyle } from '../../chart_types/bullet_graph/theme'; import type { Color } from '../../common/colors'; import type { Pixels, Radian, Ratio } from '../../common/geometry'; -import type { Font, FontStyle } from '../../common/text_utils'; +import type { Font, FontStyle, FontWeight, TextAlign } from '../../common/text_utils'; import type { ColorVariant, HorizontalAlignment, RecursivePartial, VerticalAlignment } from '../common'; import type { Margins, Padding, SimplePadding } from '../dimensions'; import type { Point } from '../point'; @@ -193,9 +193,7 @@ export interface GridLineStyle { dash: number[]; } -/** - * @public - */ +/** @public */ export interface GoalStyles { progressLine: Pick<StrokeStyle, 'stroke'>; targetLine: Pick<StrokeStyle, 'stroke'>; @@ -244,9 +242,7 @@ export interface GoalStyles { capturePad: number; } -/** - * @public - */ +/** @public */ export interface HeatmapStyle { /** * Config of the mask over the area outside of the selected cells @@ -305,15 +301,36 @@ export interface HeatmapStyle { maxLegendHeight?: number; } -/** @public */ +/** + * Metric font weight options for text styling. + * @public + */ +export type MetricFontWeight = Extract<FontWeight, 'bold' | 'normal'>; + +/** + * Style options for the Metric chart type. + * @public + */ export interface MetricStyle { textDarkColor: Color; textLightColor: Color; + textSubtitleDarkColor: Color; + textSubtitleLightColor: Color; + textExtraDarkColor: Color; + textExtraLightColor: Color; + valueFontSize: 'default' | 'fit' | number; minValueFontSize: number; - titlesTextAlign: 'left' | 'center' | 'right'; - valuesTextAlign: 'left' | 'center' | 'right'; - iconAlign: 'left' | 'right'; + + // Alignments + titlesTextAlign: Extract<TextAlign, 'left' | 'center' | 'right'>; + extraTextAlign: Extract<TextAlign, 'left' | 'center' | 'right'>; + valueTextAlign: Extract<TextAlign, 'left' | 'center' | 'right'>; + valuePosition: 'top' | 'bottom'; + iconAlign: Extract<HorizontalAlignment, 'left' | 'right'>; + + titleWeight: MetricFontWeight; + border: Color; barBackground: Color; emptyBackground: Color; diff --git a/storybook/stories/metric/1_basic.story.tsx b/storybook/stories/metric/1_basic.story.tsx index 1e23714d7bf..69b9c677edc 100644 --- a/storybook/stories/metric/1_basic.story.tsx +++ b/storybook/stories/metric/1_basic.story.tsx @@ -19,17 +19,12 @@ import type { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; import { customKnobs } from '../utils/knobs'; -type TextAlign = 'left' | 'center' | 'right'; -const getTextAlignKnob = (name: string, defaultValue: TextAlign): TextAlign => - select( - name, - { - Left: 'left', - Center: 'center', - Right: 'right', - }, - defaultValue, - ); +const getTextAlignKnob = ( + name: string, + defaultValue: 'left' | 'center' | 'right', + groupId?: string, +): 'left' | 'center' | 'right' => + select(name, { Left: 'left', Center: 'center', Right: 'right' }, defaultValue, groupId); export const Example: ChartsStory = (_, { title: storyTitle, description }) => { const title = text('title', '21d7f8b7-92ea-41a0-8c03-0db0ec7e11b9'); @@ -84,7 +79,9 @@ export const Example: ChartsStory = (_, { title: storyTitle, description }) => { ); const valueFontSize = number('value font size (px)', 40, { min: 0, step: 10 }); const titlesTextAlign = getTextAlignKnob('title text-align', 'left'); - const valuesTextAlign = getTextAlignKnob('values text-align', 'right'); + const valueTextAlign = getTextAlignKnob('value text-align', 'right'); + const extraTextAlign = getTextAlignKnob('extra text-align', 'right'); + const valuePosition = select('value position', { Bottom: 'bottom', Top: 'top' }, 'bottom'); const iconAlign = select( 'icon align', { @@ -108,6 +105,8 @@ export const Example: ChartsStory = (_, { title: storyTitle, description }) => { ...(showValueIcon ? { valueIcon: getIcon(valueIconType) } : {}), }; + const target = number('target', 75, { range: true, min: -200, max: 200 }); + const numericData: MetricWProgress | MetricWNumber | MetricWTrend = { ...data, value: Number.parseFloat(value), @@ -121,6 +120,7 @@ export const Example: ChartsStory = (_, { title: storyTitle, description }) => { trendA11yDescription, } : {}), + target, }; const textualData: MetricWText | MetricWTrend = { ...data, @@ -141,6 +141,7 @@ export const Example: ChartsStory = (_, { title: storyTitle, description }) => { const onEventOutAction = action('out'); const configuredData = [[numberTextSwitch ? numericData : textualData]]; + return ( <Chart title={storyTitle} description={description}> <Settings @@ -149,8 +150,10 @@ export const Example: ChartsStory = (_, { title: storyTitle, description }) => { blendingBackground: useBlendingBackground ? blendingBackground : undefined, valueFontSize: valueFontSizeMode === 'custom' ? valueFontSize : valueFontSizeMode, titlesTextAlign, - valuesTextAlign, + valueTextAlign, + extraTextAlign, iconAlign, + valuePosition, }, }} baseTheme={useBaseTheme()} diff --git a/storybook/stories/metric/6_extra_badge.story.tsx b/storybook/stories/metric/6_extra_badge.story.tsx index e13faf1d345..3335f31c82b 100644 --- a/storybook/stories/metric/6_extra_badge.story.tsx +++ b/storybook/stories/metric/6_extra_badge.story.tsx @@ -80,7 +80,8 @@ export const Example: ChartsStory = (_, { title: storyTitle, description }) => { ); const valueFontSize = number('value font size (px)', 40, { min: 0, step: 10 }); const titlesTextAlign = 'left'; - const valuesTextAlign = 'right'; + const valueTextAlign = 'right'; + const extraTextAlign = 'right'; const iconAlign = 'right'; const getIcon = (type: string) => @@ -154,7 +155,8 @@ export const Example: ChartsStory = (_, { title: storyTitle, description }) => { blendingBackground: useBlendingBackground ? blendingBackground : undefined, valueFontSize: valueFontSizeMode === 'custom' ? valueFontSize : valueFontSizeMode, titlesTextAlign, - valuesTextAlign, + valueTextAlign, + extraTextAlign, iconAlign, }, }} diff --git a/storybook/stories/metric/7_layout.story.tsx b/storybook/stories/metric/7_layout.story.tsx new file mode 100644 index 00000000000..7763accd08a --- /dev/null +++ b/storybook/stories/metric/7_layout.story.tsx @@ -0,0 +1,280 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EuiIcon } from '@elastic/eui'; +import { action } from '@storybook/addon-actions'; +import { select, boolean, text, color, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import type { MetricWProgress, MetricWTrend, MetricWText, MetricWNumber, SecondaryMetricProps } from '@elastic/charts'; +import { Chart, isMetricElementEvent, Metric, MetricTrendShape, Settings } from '@elastic/charts'; +import { KIBANA_METRICS } from '@elastic/charts/src/utils/data_samples/test_dataset_kibana'; + +import type { ChartsStory } from '../../types'; +import { useBaseTheme } from '../../use_base_theme'; +import { customKnobs } from '../utils/knobs'; + +const getTextAlignKnob = ( + name: string, + defaultValue: 'left' | 'center' | 'right', + groupId?: string, +): 'left' | 'center' | 'right' => + select(name, { Left: 'left', Center: 'center', Right: 'right' }, defaultValue, groupId); + +const getIcon = + (type: string) => + ({ width, height, color }: { width: number; height: number; color: string }) => ( + <EuiIcon type={type} width={width} height={height} fill={color} style={{ width, height }} /> + ); + +const textConfigurationAndPositionGroup = 'Text configuration and position'; +const secondaryMetricGroup = 'Secondary metric'; +const colorGroup = 'Colors'; + +export const Example: ChartsStory = (_, { title: storyTitle, description }) => { + // Title and subtitle values + const title = text('Title', 'Count of records'); + const subtitle = text('Subtitle', 'Little description of this component'); + + // Visualization type + const progressOrTrend = select('Visualization type', { trend: 'trend', bar: 'bar', none: 'none' }, 'bar'); + const progressBarDirection = select( + 'Progress bar direction', + { horizontal: 'horizontal', vertical: 'vertical' }, + 'vertical', + ); + + const maxDataPoints = number('Trend data points', 30, { min: 0, max: 50, step: 1 }); + const trendShape = customKnobs.fromEnum('Trend shape', MetricTrendShape, MetricTrendShape.Area); + const trendA11yTitle = text('Trend a11y title', 'The Cluster CPU Usage trend'); + const trendA11yDescription = text( + 'Trend a11y description', + 'The trend shows a peak of CPU usage in the last 5 minutes', + ); + + const progressMax = number('Progress max', 100); + const numberTextSwitch = boolean('Is numeric metric', true); + const value = text('Value', '55.23'); + const valuePrefix = text('Value prefix', ''); + const valuePostfix = text('Value postfix', ' %'); + + // Secondary metric + + const showExtra = boolean('Show extra', true, secondaryMetricGroup); + const useCustomExtra = boolean('Use custom extra', false, secondaryMetricGroup); + + let extra = text('Custom extra', 'last <b>5m</b>', secondaryMetricGroup); + extra = extra.replace('<b>', '<b>'); + extra = extra.replace('</b>', '</b>'); + + const secondaryMetricValue = text('Secondary metric value', '87.20', secondaryMetricGroup); + const secondaryMetricIcon = select( + 'Secondary metric trend icon', + { Increase: '↑', Decrease: '↓', Stable: '=', Empty: '' }, + '↑', + secondaryMetricGroup, + ); + const secondaryMetricIconPosition = select( + 'Secondary metric trend icon position', + { Before: 'before', After: 'after' }, + 'after', + secondaryMetricGroup, + ); + const label = text('Label', 'Last week', secondaryMetricGroup); + const colorByValue = boolean('Color by value', true, secondaryMetricGroup); + const badgeColor = color('Secondary metric value color', 'rgb(93, 191, 149)', secondaryMetricGroup); + const secondaryMetricLabelPosition = select( + 'Secondary metric label position', + { before: 'before', after: 'after' }, + 'before', + secondaryMetricGroup, + ); + const secondaryMetricAriaDescription = text('Aria description', 'This is a description', secondaryMetricGroup); + + const secondaryProps: SecondaryMetricProps = { + value: secondaryMetricValue, + label, + badgeColor: colorByValue ? badgeColor : undefined, + labelPosition: secondaryMetricLabelPosition, + ariaDescription: secondaryMetricAriaDescription, + icon: secondaryMetricIcon, + iconPosition: secondaryMetricIconPosition, + }; + + const dataExtra = showExtra + ? useCustomExtra + ? { extra: <span dangerouslySetInnerHTML={{ __html: extra }}></span> } + : { extra: secondaryProps } + : {}; + + // Colors + + const metricColor = color('Metric color', 'rgb(246, 217, 143)', colorGroup); + + const useValueColor = boolean('Use value color', false, colorGroup); + const valueColor = select( + 'Value color', + { + euiColorVisText0: '#065B58', + euiColorVisText1: '#047471', + euiColorVisText2: '#154399', + euiColorVisText3: '#0B64DD', + euiColorVisText4: '#A11262', + euiColorVisText5: '#D13680', + euiColorVisText6: '#A71627', + euiColorVisText7: '#DA3737', + euiColorVisText8: '#6A4906', + euiColorVisText9: '#966B03', + }, + '#065B58', + colorGroup, + ); + const useBlendingBackground = boolean('Use blending background', false, colorGroup); + const blendingBackground = color('Blending background', 'rgba(255,255,255,1)', colorGroup); + const barBackground = color('Bar background', 'rgb(194, 201, 214) ', colorGroup); + + // Metric icon + const iconType = 'warning'; + const showIcon = boolean('Show metric icon', false, textConfigurationAndPositionGroup); + const iconAlign = select( + 'Metric icon align', + { Left: 'left', Right: 'right' }, + 'right', + textConfigurationAndPositionGroup, + ); + + // Value icon + const showValueIcon = boolean('Show value icon', false); + const valueIconType = 'sortUp'; + + const data = { + color: metricColor, + title, + valueColor: useValueColor ? valueColor : undefined, + subtitle, + ...dataExtra, + ...(showIcon ? { icon: getIcon(iconType) } : {}), + ...(showValueIcon ? { valueIcon: getIcon(valueIconType) } : {}), + }; + + const numericData: MetricWProgress | MetricWNumber | MetricWTrend = { + ...data, + value: Number.parseFloat(value), + valueFormatter: (d: number) => `${valuePrefix}${d}${valuePostfix}`, + ...(progressOrTrend === 'bar' ? { domainMax: progressMax, progressBarDirection } : {}), + ...(progressOrTrend === 'trend' + ? { + trend: KIBANA_METRICS.metrics.kibana_os_load.v2.data.slice(0, maxDataPoints).map(([x, y]) => ({ x, y })), + trendShape, + trendA11yTitle, + trendA11yDescription, + } + : {}), + }; + + const textualData: MetricWText | MetricWTrend = { + ...data, + value, + ...(progressOrTrend === 'bar' ? { domainMax: progressMax, progressBarDirection } : {}), + ...(progressOrTrend === 'trend' + ? { + trend: KIBANA_METRICS.metrics.kibana_os_load.v2.data.slice(0, maxDataPoints).map(([x, y]) => ({ x, y })), + trendShape, + trendA11yTitle, + trendA11yDescription, + } + : {}), + }; + + const onEventClickAction = action('click'); + const onEventOverAction = action('over'); + const onEventOutAction = action('out'); + + const configuredData = [[numberTextSwitch ? numericData : textualData]]; + + // Configurations + + // Title and subtitle + const titlesTextAlign = getTextAlignKnob('Title and subtitle alignment', 'left', textConfigurationAndPositionGroup); + const titleWeight = select( + 'Title weight', + { Bold: 'bold', Normal: 'normal' }, + 'normal', + textConfigurationAndPositionGroup, + ); + // Value (primary metric) + const valuePosition = select( + 'Primary metric position', + { Bottom: 'bottom', Top: 'top' }, + 'top', + textConfigurationAndPositionGroup, + ); + const valueTextAlign = getTextAlignKnob('Primary metric alignment', 'left', textConfigurationAndPositionGroup); + const valueFontSizeMode = select( + 'Primary metric font size mode', + { Default: 'default', Fit: 'fit', Custom: 'custom' }, + 'default', + textConfigurationAndPositionGroup, + ); + const valueFontSize = number( + 'Primary metric font size (only if custom font size selected)', + 40, + { min: 0, step: 10 }, + textConfigurationAndPositionGroup, + ); + // Extra (secondary metric) + const extraTextAlign = getTextAlignKnob('Extra element alignment', 'left', textConfigurationAndPositionGroup); + + const baseTheme = useBaseTheme(); + + return ( + <Chart title={storyTitle} description={description}> + <Settings + theme={{ + metric: { + blendingBackground: useBlendingBackground ? blendingBackground : undefined, + valueFontSize: valueFontSizeMode === 'custom' ? valueFontSize : valueFontSizeMode, + titlesTextAlign, + valueTextAlign, + extraTextAlign, + iconAlign, + valuePosition, + titleWeight, + barBackground, + }, + }} + baseTheme={baseTheme} + onElementClick={([d]) => { + if (isMetricElementEvent(d)) { + const { rowIndex, columnIndex } = d; + onEventClickAction( + `row:${rowIndex} col:${columnIndex} value:${configuredData[rowIndex][columnIndex].value}`, + ); + } + }} + onElementOver={([d]) => { + if (isMetricElementEvent(d)) { + const { rowIndex, columnIndex } = d; + onEventOverAction( + `row:${rowIndex} col:${columnIndex} value:${configuredData[rowIndex][columnIndex].value}`, + ); + } + }} + onElementOut={() => onEventOutAction('out')} + /> + <Metric id="metric-id" data={configuredData} /> + </Chart> + ); +}; + +Example.parameters = { + resize: { + height: '200px', + width: '200px', + }, +}; diff --git a/storybook/stories/metric/metric.stories.tsx b/storybook/stories/metric/metric.stories.tsx index 22f2a14c3bf..0e2868022a2 100644 --- a/storybook/stories/metric/metric.stories.tsx +++ b/storybook/stories/metric/metric.stories.tsx @@ -16,3 +16,4 @@ export { Example as bodyContent } from './3_body.story'; export { Example as trendMultipleData } from './4_trend_bug.story'; export { Example as arrayOfValues } from './5_array_of_values.story'; export { Example as extraBadges } from './6_extra_badge.story'; +export { Example as layout } from './7_layout.story';