diff --git a/.gitignore b/.gitignore index 5fd8d08ab7..3158733b2d 100644 --- a/.gitignore +++ b/.gitignore @@ -19,5 +19,6 @@ coverage/ npm-debug.log yarn-error.log playground/playground.tsx +.jest-image-snapshot-touched-files **/__diff_output__/ diff --git a/NOTICE.txt b/NOTICE.txt index cc4d9b7301..200905a764 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -43,7 +43,7 @@ THIS SOFTWARE. --- -This product includes code that is adapted from d3-array@3.0.4 and d3-scale@4.0.2, +This product includes code that is adapted from d3-shape@3.0.1, d3-array@3.0.4 and d3-scale@4.0.2, which are both available under a "ISC" license. ISC License diff --git a/integration/page_objects/common.ts b/integration/page_objects/common.ts index 465d0f3523..5d6e389082 100644 --- a/integration/page_objects/common.ts +++ b/integration/page_objects/common.ts @@ -362,9 +362,12 @@ class CommonPage { async expectChartWithMouseAtUrlToMatchScreenshot( url: string, mousePosition: MousePosition, - options?: Omit, + options?: ScreenshotElementAtUrlOptions, ) { - const action = async () => await this.moveMouseRelativeToDOMElement(mousePosition, this.chartSelector); + const action = async () => { + await options?.action?.(); + await this.moveMouseRelativeToDOMElement(mousePosition, this.chartSelector); + }; await this.expectChartAtUrlToMatchScreenshot(url, { ...options, action, diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-band-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-band-visually-looks-correct-1-snap.png deleted file mode 100644 index bac213c4e3..0000000000 Binary files a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-band-visually-looks-correct-1-snap.png and /dev/null differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-mixed-charts-polarized-stacked-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-mixed-charts-polarized-stacked-visually-looks-correct-1-snap.png new file mode 100644 index 0000000000..7868f74888 Binary files /dev/null and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-mixed-charts-polarized-stacked-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/area-stories-test-ts-area-series-stories-scale-to-extents-domain-fit-is-false-should-show-correct-extents-banded-1-snap.png b/integration/tests/__image_snapshots__/area-stories-test-ts-area-series-stories-scale-to-extents-domain-fit-is-false-should-show-correct-extents-banded-1-snap.png deleted file mode 100644 index bac213c4e3..0000000000 Binary files a/integration/tests/__image_snapshots__/area-stories-test-ts-area-series-stories-scale-to-extents-domain-fit-is-false-should-show-correct-extents-banded-1-snap.png and /dev/null differ diff --git a/integration/tests/__image_snapshots__/area-stories-test-ts-area-series-stories-scale-to-extents-domain-fit-is-true-should-show-correct-extents-banded-1-snap.png b/integration/tests/__image_snapshots__/area-stories-test-ts-area-series-stories-scale-to-extents-domain-fit-is-true-should-show-correct-extents-banded-1-snap.png deleted file mode 100644 index bac213c4e3..0000000000 Binary files a/integration/tests/__image_snapshots__/area-stories-test-ts-area-series-stories-scale-to-extents-domain-fit-is-true-should-show-correct-extents-banded-1-snap.png and /dev/null differ diff --git a/integration/tests/__image_snapshots__/axis-stories-test-ts-axis-stories-should-switch-to-a-30-minute-raster-1-snap.png b/integration/tests/__image_snapshots__/axis-stories-test-ts-axis-stories-should-switch-to-a-30-minute-raster-1-snap.png deleted file mode 100644 index 4927af7f76..0000000000 Binary files a/integration/tests/__image_snapshots__/axis-stories-test-ts-axis-stories-should-switch-to-a-30-minute-raster-1-snap.png and /dev/null differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-mixed-area-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-mixed-area-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..04f563688f Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-mixed-area-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-mixed-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-mixed-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..dee5bc6c5a Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-mixed-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-mixed-bar-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-mixed-bar-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..a85a23061c Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-mixed-bar-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-mixed-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-mixed-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..5fc4d88307 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-mixed-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-negative-area-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-negative-area-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..bd6bd0f6fc Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-negative-area-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-negative-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-negative-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..b0370b85f6 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-negative-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-negative-bar-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-negative-bar-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..43b92e95c6 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-negative-bar-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-negative-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-negative-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..513a1af986 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-negative-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-positive-area-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-positive-area-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..bd6bd0f6fc Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-positive-area-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-positive-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-positive-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..b0370b85f6 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-positive-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-positive-bar-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-positive-bar-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..43b92e95c6 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-positive-bar-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-positive-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-positive-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..513a1af986 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-percentage-polarity-positive-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-mixed-area-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-mixed-area-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..8807d2538b Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-mixed-area-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-mixed-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-mixed-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..f43b496407 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-mixed-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-mixed-bar-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-mixed-bar-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..a30de39f1c Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-mixed-bar-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-mixed-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-mixed-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..22fa4bb341 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-mixed-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-negative-area-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-negative-area-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..a856ea9f53 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-negative-area-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-negative-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-negative-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..1b07efbc86 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-negative-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-negative-bar-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-negative-bar-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..60695c3105 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-negative-bar-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-negative-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-negative-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..9fb2456a44 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-negative-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-positive-area-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-positive-area-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..44792cea25 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-positive-area-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-positive-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-positive-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..16f5e1ead8 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-positive-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-positive-bar-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-positive-bar-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..fae8a6b050 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-positive-bar-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-positive-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-positive-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..56e81e3f7d Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-silhouette-polarity-positive-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-mixed-area-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-mixed-area-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..d6ee8cc8ff Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-mixed-area-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-mixed-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-mixed-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..02f0b2aa99 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-mixed-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-mixed-bar-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-mixed-bar-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..e23970b241 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-mixed-bar-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-mixed-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-mixed-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..ae58a0dfc3 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-mixed-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-negative-area-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-negative-area-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..6555111cd9 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-negative-area-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-negative-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-negative-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..5cd5db3099 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-negative-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-negative-bar-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-negative-bar-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..12488cf19c Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-negative-bar-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-negative-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-negative-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..510c5bdad2 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-negative-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-positive-area-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-positive-area-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..6cd71693de Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-positive-area-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-positive-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-positive-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..1efde0a5bf Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-positive-area-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-positive-bar-series-should-display-correct-stacking-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-positive-bar-series-should-display-correct-stacking-1-snap.png new file mode 100644 index 0000000000..8169d61f13 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-positive-bar-series-should-display-correct-stacking-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-positive-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-positive-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png new file mode 100644 index 0000000000..215ae8d501 Binary files /dev/null and b/integration/tests/__image_snapshots__/mixed-stories-test-ts-mixed-series-stories-stack-mode-wiggle-polarity-positive-bar-series-should-show-area-chart-with-toggled-series-and-mouse-over-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-annotation-marker-rotation-should-render-marker-with-annotations-with-0-degree-rotations-1-snap.png b/integration/tests/__image_snapshots__/test-cases-stories-test-ts-annotation-marker-rotation-should-render-marker-with-annotations-with-0-degree-rotations-1-snap.png deleted file mode 100644 index e6655fdf1d..0000000000 Binary files a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-annotation-marker-rotation-should-render-marker-with-annotations-with-0-degree-rotations-1-snap.png and /dev/null differ diff --git a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-annotation-marker-rotation-should-render-marker-with-annotations-with-180-degree-rotations-1-snap.png b/integration/tests/__image_snapshots__/test-cases-stories-test-ts-annotation-marker-rotation-should-render-marker-with-annotations-with-180-degree-rotations-1-snap.png deleted file mode 100644 index 2f3656c0ae..0000000000 Binary files a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-annotation-marker-rotation-should-render-marker-with-annotations-with-180-degree-rotations-1-snap.png and /dev/null differ diff --git a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-annotation-marker-rotation-should-render-marker-with-annotations-with-90-degree-rotations-1-snap.png b/integration/tests/__image_snapshots__/test-cases-stories-test-ts-annotation-marker-rotation-should-render-marker-with-annotations-with-90-degree-rotations-1-snap.png deleted file mode 100644 index ac6fa6f768..0000000000 Binary files a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-annotation-marker-rotation-should-render-marker-with-annotations-with-90-degree-rotations-1-snap.png and /dev/null differ diff --git a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-annotation-marker-rotation-should-render-marker-with-annotations-with-negative-90-degree-rotations-1-snap.png b/integration/tests/__image_snapshots__/test-cases-stories-test-ts-annotation-marker-rotation-should-render-marker-with-annotations-with-negative-90-degree-rotations-1-snap.png deleted file mode 100644 index 397429fa4c..0000000000 Binary files a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-annotation-marker-rotation-should-render-marker-with-annotations-with-negative-90-degree-rotations-1-snap.png and /dev/null differ diff --git a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-occlusion-of-points-outside-of-chart-domain-should-not-display-tooltip-over-point-outside-of-domain-even-more-left-1-snap.png b/integration/tests/__image_snapshots__/test-cases-stories-test-ts-occlusion-of-points-outside-of-chart-domain-should-not-display-tooltip-over-point-outside-of-domain-even-more-left-1-snap.png deleted file mode 100644 index c47bf4e922..0000000000 Binary files a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-occlusion-of-points-outside-of-chart-domain-should-not-display-tooltip-over-point-outside-of-domain-even-more-left-1-snap.png and /dev/null differ diff --git a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-occlusion-of-points-outside-of-chart-domain-should-render-area-chart-with-points-outside-of-domain-correclty-1-snap.png b/integration/tests/__image_snapshots__/test-cases-stories-test-ts-occlusion-of-points-outside-of-chart-domain-should-render-area-chart-with-points-outside-of-domain-correclty-1-snap.png deleted file mode 100644 index e2e5c1e0a1..0000000000 Binary files a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-occlusion-of-points-outside-of-chart-domain-should-render-area-chart-with-points-outside-of-domain-correclty-1-snap.png and /dev/null differ diff --git a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-occlusion-of-points-outside-of-chart-domain-should-render-area-chart-with-points-outside-of-the-domain-with-y-0-accessor-correctly-1-snap.png b/integration/tests/__image_snapshots__/test-cases-stories-test-ts-occlusion-of-points-outside-of-chart-domain-should-render-area-chart-with-points-outside-of-the-domain-with-y-0-accessor-correctly-1-snap.png deleted file mode 100644 index c6bf85be3e..0000000000 Binary files a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-occlusion-of-points-outside-of-chart-domain-should-render-area-chart-with-points-outside-of-the-domain-with-y-0-accessor-correctly-1-snap.png and /dev/null differ diff --git a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-occlusion-of-points-outside-of-chart-domain-should-render-line-chart-with-points-outside-of-domain-correctly-1-snap.png b/integration/tests/__image_snapshots__/test-cases-stories-test-ts-occlusion-of-points-outside-of-chart-domain-should-render-line-chart-with-points-outside-of-domain-correctly-1-snap.png deleted file mode 100644 index 5171f9a3e5..0000000000 Binary files a/integration/tests/__image_snapshots__/test-cases-stories-test-ts-occlusion-of-points-outside-of-chart-domain-should-render-line-chart-with-points-outside-of-domain-correctly-1-snap.png and /dev/null differ diff --git a/integration/tests/area_stories.test.ts b/integration/tests/area_stories.test.ts index 45c99355f2..aca64cde48 100644 --- a/integration/tests/area_stories.test.ts +++ b/integration/tests/area_stories.test.ts @@ -23,22 +23,6 @@ describe('Area series stories', () => { }); }); - describe('scale to extents', () => { - describe('domain.fit is true', () => { - const trueUrl = 'http://localhost:9001/?path=/story/area-chart--stacked-band&knob-fit Y domain=true'; - it('should show correct extents - Banded', async () => { - await common.expectChartAtUrlToMatchScreenshot(trueUrl); - }); - }); - - describe('domain.fit is false', () => { - const falseUrl = 'http://localhost:9001/?path=/story/area-chart--stacked-band&knob-fit Y domain=false'; - - it('should show correct extents - Banded', async () => { - await common.expectChartAtUrlToMatchScreenshot(falseUrl); - }); - }); - }); describe('Non-Stacked Linear Area with discontinuous data points', () => { it('with fit', async () => { await common.expectChartAtUrlToMatchScreenshot( diff --git a/integration/tests/mixed_stories.test.ts b/integration/tests/mixed_stories.test.ts index 17fbca014e..fec54048ed 100644 --- a/integration/tests/mixed_stories.test.ts +++ b/integration/tests/mixed_stories.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Fit } from '../../packages/charts/src'; +import { Fit, StackMode, SeriesType } from '../../packages/charts/src'; import { common } from '../page_objects'; describe('Mixed series stories', () => { @@ -195,4 +195,27 @@ describe('Mixed series stories', () => { }); }); }); + + describe.each(Object.values(StackMode))('Stack mode - %s', (mode) => { + describe.each(['Mixed', 'Positive', 'Negative'])('Polarity - %s', (polarity) => { + describe.each([SeriesType.Bar, SeriesType.Area])('%s series', (type) => { + it('should display correct stacking', async () => { + await common.expectChartAtUrlToMatchScreenshot( + `http://localhost:9001/?path=/story/mixed-charts---polarized-stacked&globals=theme:light&knob-stacked=true&knob-data polarity=${polarity}&knob-custom domain=false&knob-stackMode=${mode}&knob-SeriesType=${type}`, + ); + }); + + it('should show area chart with toggled series and mouse over', async () => { + const action = async () => { + await page.click('.echLegendItem:nth-child(2) .echLegendItem__label'); + }; + await common.expectChartWithMouseAtUrlToMatchScreenshot( + `http://localhost:9001/?path=/story/mixed-charts---polarized-stacked&globals=theme:light&knob-stacked=true&knob-data polarity=${polarity}&knob-custom domain=false&knob-stackMode=${mode}&knob-SeriesType=${type}`, + { top: 170, left: 490 }, + { action }, + ); + }); + }); + }); + }); }); diff --git a/package.json b/package.json index 0e717c77ce..d8feec43ed 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "@types/d3-collection": "^1.0.8", "@types/d3-interpolate": "^1.3.1", "@types/d3-scale": "^2.1.1", - "@types/d3-shape": "^1.3.1", + "@types/d3-shape": "^2.0.0", "@types/enzyme": "^3.9.0", "@types/enzyme-adapter-react-16": "^1.0.5", "@types/expect-puppeteer": "^4.4.5", diff --git a/packages/charts/package.json b/packages/charts/package.json index 59b538fc94..ad60045cef 100644 --- a/packages/charts/package.json +++ b/packages/charts/package.json @@ -40,7 +40,7 @@ "d3-collection": "^1.0.7", "d3-interpolate": "^1.4.0", "d3-scale": "^1.0.7", - "d3-shape": "^1.3.4", + "d3-shape": "^2.0.0", "prop-types": "^15.7.2", "re-reselect": "^3.4.0", "react-redux": "^7.1.0", diff --git a/packages/charts/src/chart_types/xy_chart/domains/y_domain.ts b/packages/charts/src/chart_types/xy_chart/domains/y_domain.ts index 94b6e301aa..93b39fcfc6 100644 --- a/packages/charts/src/chart_types/xy_chart/domains/y_domain.ts +++ b/packages/charts/src/chart_types/xy_chart/domains/y_domain.ts @@ -47,14 +47,14 @@ function mergeYDomainForGroup( const dataSeries = [...stacked, ...nonStacked]; if (dataSeries.length === 0) return null; - const [{ stackMode, spec }] = dataSeries; + const [{ isStacked, stackMode, spec }] = dataSeries; const groupId = getSpecDomainGroupId(spec); const { customDomain, type, nice, desiredTickCount } = yScaleConfig[groupId]; const newCustomDomain: YDomainRange = customDomain ? { ...customDomain } : { min: NaN, max: NaN }; const { paddingUnit, padding, constrainPadding } = newCustomDomain; let mergedDomain: ContinuousDomain; - if (stackMode === StackMode.Percentage) { + if (isStacked && stackMode === StackMode.Percentage) { mergedDomain = computeContinuousDataDomain([0, 1], type, customDomain); } else { const stackedDomain = computeYDomain(stacked, hasZeroBaselineSpecs, type, newCustomDomain); diff --git a/packages/charts/src/chart_types/xy_chart/legend/legend.ts b/packages/charts/src/chart_types/xy_chart/legend/legend.ts index 2845f7a91e..ea5b038223 100644 --- a/packages/charts/src/chart_types/xy_chart/legend/legend.ts +++ b/packages/charts/src/chart_types/xy_chart/legend/legend.ts @@ -27,7 +27,7 @@ import { getSeriesName, DataSeries, getSeriesKey, - isDataSeriesBanded, + isBandedSpec, getSeriesIdentifierFromDataSeries, } from '../utils/series'; import { @@ -111,7 +111,7 @@ export function computeLegend( dataSeries.forEach((series) => { const { specId, yAccessor } = series; - const banded = isDataSeriesBanded(series); + const banded = isBandedSpec(series.spec); const key = getSeriesKey(series, series.groupId); const spec = getSpecsById(specs, specId); const dataSeriesKey = getSeriesKey( diff --git a/packages/charts/src/chart_types/xy_chart/rendering/area.ts b/packages/charts/src/chart_types/xy_chart/rendering/area.ts index 969e6dfe18..22bbb4e0d9 100644 --- a/packages/charts/src/chart_types/xy_chart/rendering/area.ts +++ b/packages/charts/src/chart_types/xy_chart/rendering/area.ts @@ -36,7 +36,7 @@ export function renderArea( panel: Dimensions, color: Color, curve: CurveType, - hasY0Accessors: boolean, + isBandedSpec: boolean, xScaleOffset: number, seriesStyle: AreaSeriesStyle, markSizeOptions: MarkSizeOptions, @@ -57,14 +57,14 @@ export function renderArea( .y1(y1Fn) .y0(y0Fn) .defined((datum) => { - return definedFn(datum, y1DatumAccessor) && (hasY0Accessors ? definedFn(datum, y0DatumAccessor) : true); + return definedFn(datum, y1DatumAccessor) && (isBandedSpec ? definedFn(datum, y0DatumAccessor) : true); }) .curve(getCurveFactory(curve)); const clippedRanges = getClippedRanges(dataSeries.data, xScale, xScaleOffset); const lines: string[] = []; - const y0Line = hasY0Accessors && pathGenerator.lineY0()(dataSeries.data); + const y0Line = isBandedSpec && pathGenerator.lineY0()(dataSeries.data); const y1Line = pathGenerator.lineY1()(dataSeries.data); if (y1Line) lines.push(y1Line); if (y0Line) lines.push(y0Line); @@ -77,7 +77,7 @@ export function renderArea( panel, color, seriesStyle.point, - hasY0Accessors, + isBandedSpec, markSizeOptions, pointStyleAccessor, false, diff --git a/packages/charts/src/chart_types/xy_chart/rendering/points.ts b/packages/charts/src/chart_types/xy_chart/rendering/points.ts index 9f1ab85fae..c724d7d190 100644 --- a/packages/charts/src/chart_types/xy_chart/rendering/points.ts +++ b/packages/charts/src/chart_types/xy_chart/rendering/points.ts @@ -35,7 +35,7 @@ export function renderPoints( panel: Dimensions, color: Color, pointStyle: PointStyle, - hasY0Accessors: boolean, + isBandChart: boolean, markSizeOptions: MarkSizeOptions, styleAccessor?: PointStyleAccessor, spatial = false, @@ -66,12 +66,12 @@ export function renderPoints( if (Number.isNaN(x)) return acc; const points: PointGeometry[] = []; - const yDatumKeyNames: Array> = hasY0Accessors ? ['y0', 'y1'] : ['y1']; + const yDatumKeyNames: Array> = isBandChart ? ['y0', 'y1'] : ['y1']; yDatumKeyNames.forEach((yDatumKeyName, keyIndex) => { const valueAccessor = getYDatumValueFn(yDatumKeyName); const y = yDatumKeyName === 'y1' ? y1Fn(datum) : y0Fn(datum); - const originalY = getDatumYValue(datum, keyIndex === 0, hasY0Accessors, dataSeries.stackMode); + const originalY = getDatumYValue(datum, keyIndex === 0, isBandChart, dataSeries.stackMode); const seriesIdentifier: XYChartSeriesIdentifier = { key: dataSeries.key, specId: dataSeries.specId, @@ -98,7 +98,7 @@ export function renderPoints( x: xValue, y: originalY, mark, - accessor: hasY0Accessors && keyIndex === 0 ? BandedAccessorType.Y0 : BandedAccessorType.Y1, + accessor: isBandChart && keyIndex === 0 ? BandedAccessorType.Y0 : BandedAccessorType.Y1, datum: datum.datum, }, transform: { diff --git a/packages/charts/src/chart_types/xy_chart/state/utils/utils.ts b/packages/charts/src/chart_types/xy_chart/state/utils/utils.ts index 2d2fed96cd..6df8cbee9c 100644 --- a/packages/charts/src/chart_types/xy_chart/state/utils/utils.ts +++ b/packages/charts/src/chart_types/xy_chart/state/utils/utils.ts @@ -37,7 +37,13 @@ import { fillSeries } from '../../utils/fill_series'; import { groupBy } from '../../utils/group_data_series'; import { IndexedGeometryMap } from '../../utils/indexed_geometry_map'; import { computeXScale, computeYScales } from '../../utils/scales'; -import { DataSeries, getDataSeriesFromSpecs, getFormattedDataSeries, getSeriesKey } from '../../utils/series'; +import { + DataSeries, + getDataSeriesFromSpecs, + getFormattedDataSeries, + getSeriesKey, + isBandedSpec, +} from '../../utils/series'; import { AxisSpec, BasicSeriesSpec, @@ -46,7 +52,6 @@ import { HistogramModeAlignment, HistogramModeAlignments, isAreaSeriesSpec, - isBandedSpec, isBarSeriesSpec, isBubbleSeriesSpec, isLineSeriesSpec, @@ -384,7 +389,7 @@ function renderGeometries( yScale, color, panel, - isBandedSpec(spec.y0Accessors), + isBandedSpec(spec), xScaleOffset, bubbleSeriesStyle, { @@ -418,7 +423,7 @@ function renderGeometries( panel, color, spec.curve || CurveType.LINEAR, - isBandedSpec(spec.y0Accessors), + isBandedSpec(spec), xScaleOffset, lineSeriesStyle, { @@ -451,7 +456,7 @@ function renderGeometries( panel, color, spec.curve || CurveType.LINEAR, - isBandedSpec(spec.y0Accessors), + isBandedSpec(spec), xScaleOffset, areaSeriesStyle, { diff --git a/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts b/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts index e3e063d5fe..336a0aabad 100644 --- a/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts +++ b/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts @@ -13,15 +13,8 @@ import { getAccessorFormatLabel } from '../../../utils/accessor'; import { isDefined } from '../../../utils/common'; import { BandedAccessorType, IndexedGeometry } from '../../../utils/geometry'; import { defaultTickFormatter } from '../utils/axis_utils'; -import { getSeriesName } from '../utils/series'; -import { - AxisSpec, - BasicSeriesSpec, - isAreaSeriesSpec, - isBandedSpec, - isBarSeriesSpec, - TickFormatterOptions, -} from '../utils/specs'; +import { getSeriesName, isBandedSpec } from '../utils/series'; +import { AxisSpec, BasicSeriesSpec, isAreaSeriesSpec, isBarSeriesSpec, TickFormatterOptions } from '../utils/specs'; /** @internal */ export const Y0_ACCESSOR_POSTFIX = ' - lower'; @@ -66,7 +59,7 @@ export function formatTooltip( ): TooltipValue { let label = getSeriesName(seriesIdentifier, hasSingleSeries, true, spec); - if (isBandedSpec(spec.y0Accessors) && (isAreaSeriesSpec(spec) || isBarSeriesSpec(spec))) { + if (isBandedSpec(spec) && (isAreaSeriesSpec(spec) || isBarSeriesSpec(spec))) { const { y0AccessorFormat = Y0_ACCESSOR_POSTFIX, y1AccessorFormat = Y1_ACCESSOR_POSTFIX } = spec; const formatter = accessor === BandedAccessorType.Y0 ? y0AccessorFormat : y1AccessorFormat; label = getAccessorFormatLabel(formatter, label); diff --git a/packages/charts/src/chart_types/xy_chart/utils/__snapshots__/series.test.ts.snap b/packages/charts/src/chart_types/xy_chart/utils/__snapshots__/series.test.ts.snap index 80a56fac20..6b0973afd4 100644 --- a/packages/charts/src/chart_types/xy_chart/utils/__snapshots__/series.test.ts.snap +++ b/packages/charts/src/chart_types/xy_chart/utils/__snapshots__/series.test.ts.snap @@ -22663,7 +22663,7 @@ Array [ "initialY1": 3, "mark": null, "x": 1, - "y0": 1, + "y0": 0, "y1": 3, }, Object { @@ -22678,7 +22678,7 @@ Array [ "initialY1": 3, "mark": null, "x": 2, - "y0": 2, + "y0": 0, "y1": 3, }, Object { @@ -22705,7 +22705,7 @@ Array [ "initialY1": 4, "mark": null, "x": 4, - "y0": 3, + "y0": 0, "y1": 4, }, ], @@ -22734,7 +22734,7 @@ Array [ "initialY1": 2, "mark": null, "x": 1, - "y0": 4, + "y0": 3, "y1": 5, }, Object { @@ -22749,7 +22749,7 @@ Array [ "initialY1": 3, "mark": null, "x": 2, - "y0": 4, + "y0": 3, "y1": 6, }, Object { @@ -22764,7 +22764,7 @@ Array [ "initialY1": 23, "mark": null, "x": 3, - "y0": 4, + "y0": 0, "y1": 23, }, Object { @@ -22779,7 +22779,7 @@ Array [ "initialY1": 4, "mark": null, "x": 4, - "y0": 5, + "y0": 4, "y1": 8, }, ], @@ -22813,7 +22813,7 @@ Array [ "initialY1": 3, "mark": null, "x": 1, - "y0": 1, + "y0": 0, "y1": 3, }, Object { @@ -22828,7 +22828,7 @@ Array [ "initialY1": 3, "mark": null, "x": 2, - "y0": 2, + "y0": 0, "y1": 3, }, Object { @@ -22855,7 +22855,7 @@ Array [ "initialY1": 4, "mark": null, "x": 4, - "y0": 3, + "y0": 0, "y1": 4, }, ], @@ -22884,7 +22884,7 @@ Array [ "initialY1": 2, "mark": null, "x": 1, - "y0": 4, + "y0": 3, "y1": 5, }, Object { @@ -22899,7 +22899,7 @@ Array [ "initialY1": 3, "mark": null, "x": 2, - "y0": 4, + "y0": 3, "y1": 6, }, Object { @@ -22914,7 +22914,7 @@ Array [ "initialY1": 23, "mark": null, "x": 3, - "y0": 4, + "y0": 0, "y1": 23, }, Object { @@ -22929,7 +22929,7 @@ Array [ "initialY1": 4, "mark": null, "x": 4, - "y0": 5, + "y0": 4, "y1": 8, }, ], diff --git a/packages/charts/src/chart_types/xy_chart/utils/diverging_offsets.ts b/packages/charts/src/chart_types/xy_chart/utils/diverging_offsets.ts new file mode 100644 index 0000000000..8f0b8a5959 --- /dev/null +++ b/packages/charts/src/chart_types/xy_chart/utils/diverging_offsets.ts @@ -0,0 +1,202 @@ +/* eslint-disable header/header, no-param-reassign */ + +/** + * @notice + * This product includes code that is adapted from d3-shape@3.0.1, + * which is available under a "ISC" license. + * + * ISC License + * + * Copyright 2010-2021 Mike Bostock + * Permission to use, copy, modify, and/or distribute this software for any purpose + * with or without fee is hereby granted, provided that the above copyright notice + * and this permission notice appear in all copies. + + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ + +import { Series, SeriesPoint } from 'd3-shape'; + +import { SeriesKey } from '../../../common/series_id'; +import { DataSeriesDatum } from './series'; + +type XValue = string | number; +type SeriesValueMap = Map; + +/** @internal */ +export type XValueMap = Map; +/** @internal */ +export type XValueSeriesDatum = [XValue, SeriesValueMap]; + +/** + * Computes required wiggle offset for each x value __WITHOUT__ mutations + */ +function getWiggleOffsets(series: Series, order: number[]): number[] { + const offsets = []; + let y, j; + for (y = 0, j = 1; j < series[order[0]].length; ++j) { + let i, s1, s2; + for (i = 0, s1 = 0, s2 = 0; i < series.length; ++i) { + // @ts-ignore - d3-shape type here is inaccurate + const si = series[order[i]] as SeriesPoint[]; + const sij0 = si[j][1] || 0; + const sij1 = si[j - 1][1] || 0; + let s3 = (sij0 - sij1) / 2; + + for (let k = 0; k < i; ++k) { + // @ts-ignore - d3-shape type here is inaccurate + const sk = series[order[k]] as SeriesPoint[]; + const skj0 = sk[j][1] || 0; + const skj1 = sk[j - 1][1] || 0; + s3 += skj0 - skj1; + } + s1 += sij0; + s2 += s3 * sij0; + } + + offsets.push(y); + if (s1) y -= s2 / s1; + } + offsets.push(y); + return offsets; +} + +/** @internal */ +const divergingOffset = (isSilhouette = false) => { + return function (series: Series, order: number[]): void { + const n = series.length; + if (!(n > 0)) return; + for (let i, j = 0, sumYn, sumYp, yp, yn = 0, s0 = series[order[0]], m = s0.length; j < m; ++j) { + // sum negative values per x before to maintain original sort for negative values + for (yn = 0, sumYn = 0, sumYp = 0, i = 0; i < n; ++i) { + // @ts-ignore - d3-shape type here is inaccurate + const d = series[order[i]][j] as SeriesPoint; + const dy = d[1] - d[0]; + if (dy < 0) { + sumYn += Math.abs(d[1]) || 0; + yn += dy; + } else { + sumYp += d[1] || 0; + } + } + + const silhouetteOffset = sumYp / 2 - sumYn / 2; + const offset = isSilhouette ? -silhouetteOffset : 0; + yn += offset; + + for (yp = offset, i = 0; i < n; ++i) { + // @ts-ignore - d3-shape type here is inaccurate + const d = series[order[i]][j] as SeriesPoint; + const dy = d[1] - d[0]; + if (dy >= 0) { + d[0] = yp; + d[1] = yp += dy; + } else { + d[1] = yn; + d[0] = yn -= dy; + } + } + } + }; +}; + +/** + * Stacked offset function with diverging polarity offset + * @internal + */ +export const diverging = divergingOffset(); +/** + * Stacked Silhouette offset function with diverging polarity offset + * @internal + */ +export const divergingSilhouette = divergingOffset(true); + +/** + * Stacked Wiggle offset function to account for diverging offset + * @internal + */ +export function divergingWiggle(series: Series, order: number[]): void { + const n = series.length; + const s0 = series[order[0]]; + const m = s0.length; + if (!(n > 0) || !(m > 0)) return diverging(series, order); + + const offsets = getWiggleOffsets(series, order); + + for (let i, j = 0, sumYn, yp, yn = 0; j < m; ++j) { + // sum negative values per x before to maintain original sort for negative values + for (i = 0, yn = 0, sumYn = 0; i < n; ++i) { + // @ts-ignore - d3-shape type here is inaccurate + const d = series[order[i]][j] as SeriesPoint; + if (d[1] - d[0] < 0) { + sumYn += Math.abs(d[1]) || 0; + } + } + + const offset = offsets[j]; + yn += offset; + + for (yp = offset + sumYn, yn = offset, i = 0; i < n; ++i) { + // @ts-ignore - d3-shape type here is inaccurate + const d = series[order[i]][j] as SeriesPoint; + const dy = d[1] - d[0]; + if (dy >= 0) { + d[0] = yp; + d[1] = yp += dy; + } else { + d[1] = yn; + d[0] = yn -= dy; + } + } + } +} + +/** + * Stacked Percentage offset function with diverging polarity offset + * Treats percentage as participation for mixed polarity data + * @internal + */ +export function divergingPercentage(series: Series, order: number[]): void { + const n = series.length; + if (!(n > 0)) return; + for (let i, j = 0, sumYn, sumYp; j < series[0].length; ++j) { + for (sumYn = sumYp = i = 0; i < n; ++i) { + // @ts-ignore - d3-shape type here is inaccurate + const d = series[order[i]][j] as SeriesPoint; + if (d[1] - d[0] < 0) { + sumYn += Math.abs(d[1]) || 0; + } else { + sumYp += d[1] || 0; + } + } + + const sumY = sumYn + sumYp; + if (sumY === 0) continue; // must not return, else loop will stop + + let yp = sumYn / sumY; + let yn = 0; + + for (i = 0; i < n; ++i) { + // @ts-ignore - d3-shape type here is inaccurate + const d = series[order[i]][j] as SeriesPoint; + const dy = d[1] - d[0]; + const participation = Math.abs(dy / sumY); + + if (dy >= 0) { + d[0] = yp; + d[1] = yp += participation; + } else { + d[0] = yn; + d[1] = yn += participation; + } + } + } +} + +/* eslint-enable header/header, no-param-reassign */ diff --git a/packages/charts/src/chart_types/xy_chart/utils/series.test.ts b/packages/charts/src/chart_types/xy_chart/utils/series.test.ts index 16839174ae..dba3f1567d 100644 --- a/packages/charts/src/chart_types/xy_chart/utils/series.test.ts +++ b/packages/charts/src/chart_types/xy_chart/utils/series.test.ts @@ -27,10 +27,10 @@ import { getSeriesColors, getDataSeriesFromSpecs, XYChartSeriesIdentifier, - extractYAndMarkFromDatum, getSeriesName, DataSeries, splitSeriesDataByAccessors, + extractYAndMarkFromDatum, } from './series'; import { BasicSeriesSpec, LineSeriesSpec, SeriesType, AreaSeriesSpec } from './specs'; import { formatStackedDataSeriesValues } from './stacked_series_utils'; @@ -236,7 +236,7 @@ describe('Series', () => { }), ]; const xValues = new Set([1, 2, 3, 4]); - const stackedValues = formatStackedDataSeriesValues(dataSeries, xValues); + const stackedValues = formatStackedDataSeriesValues(dataSeries, xValues, 'bar'); expect(stackedValues.map(matchOnlyDataSeriesLegacySnapshot)).toMatchSnapshot(); }); test('Can stack unsorted dataseries', () => { @@ -287,7 +287,7 @@ describe('Series', () => { }), ]; const xValues = new Set(new Array(maxArrayItems).fill(0).map((d, i) => i)); - const stackedValues = formatStackedDataSeriesValues(dataSeries, xValues); + const stackedValues = formatStackedDataSeriesValues(dataSeries, xValues, 'bar'); expect(stackedValues.map(matchOnlyDataSeriesLegacySnapshot)).toMatchSnapshot(); }); test('Can stack simple dataseries with scale to extent', () => { @@ -599,26 +599,26 @@ describe('Series', () => { }); test('clean datum shall parse string as number for y values', () => { - let datum = extractYAndMarkFromDatum([0, 1, 2], 1, [], 2); + let datum = extractYAndMarkFromDatum([0, 1, 2], 1, [], false, 2); expect(datum).toBeDefined(); expect(datum?.y1).toBe(1); - expect(datum?.y0).toBe(2); - datum = extractYAndMarkFromDatum([0, '1', 2], 1, [], 2); + expect(datum?.y0).toBe(null); + datum = extractYAndMarkFromDatum([0, '1', 2], 1, [], false, 2); expect(datum).toBeDefined(); expect(datum?.y1).toBe(1); - expect(datum?.y0).toBe(2); + expect(datum?.y0).toBe(null); - datum = extractYAndMarkFromDatum([0, '1', '2'], 1, [], 2); + datum = extractYAndMarkFromDatum([0, '1', '2'], 1, [], false, 2); expect(datum).toBeDefined(); expect(datum?.y1).toBe(1); - expect(datum?.y0).toBe(2); + expect(datum?.y0).toBe(null); - datum = extractYAndMarkFromDatum([0, 1, '2'], 1, [], 2); + datum = extractYAndMarkFromDatum([0, 1, '2'], 1, [], false, 2); expect(datum).toBeDefined(); expect(datum?.y1).toBe(1); - expect(datum?.y0).toBe(2); + expect(datum?.y0).toBe(null); - datum = extractYAndMarkFromDatum([0, 'invalid', 'invalid'], 1, [], 2); + datum = extractYAndMarkFromDatum([0, 'invalid', 'invalid'], 1, [], false, 2); expect(datum).toBeDefined(); expect(datum?.y1).toBe(null); expect(datum?.y0).toBe(null); diff --git a/packages/charts/src/chart_types/xy_chart/utils/series.ts b/packages/charts/src/chart_types/xy_chart/utils/series.ts index 01d3831266..5fba386ce6 100644 --- a/packages/charts/src/chart_types/xy_chart/utils/series.ts +++ b/packages/charts/src/chart_types/xy_chart/utils/series.ts @@ -131,6 +131,12 @@ export function splitSeriesDataByAccessors( const xValues: Array = []; const nonNumericValues: any[] = []; + if (isStacked && Boolean(y0Accessors?.length)) { + Logger.warn( + `y0Accessors are not allowed with stackAccessors. y0Accessors will be ignored but available under initialY0.`, + ); + } + for (let i = 0; i < data.length; i++) { const datum = data[i]; const splitAccessors = getSplitAccessors(datum, splitSeriesAccessors); @@ -161,6 +167,7 @@ export function splitSeriesDataByAccessors( datum, accessor, nonNumericValues, + isBandedSpec(spec), y0Accessors && y0Accessors[index], markSizeAccessor, ); @@ -270,6 +277,7 @@ export function extractYAndMarkFromDatum( datum: Datum, yAccessor: Accessor | AccessorFn, nonNumericValues: any[], + bandedSpec: boolean, y0Accessor?: Accessor | AccessorFn, markSizeAccessor?: Accessor | AccessorFn, ): Pick { @@ -278,7 +286,7 @@ export function extractYAndMarkFromDatum( const y1Value = getAccessorValue(datum, yAccessor); const y1 = finiteOrNull(y1Value, nonNumericValues); const y0 = y0Accessor ? finiteOrNull(getAccessorValue(datum, y0Accessor), nonNumericValues) : null; - return { y1, datum, y0, mark, initialY0: y0, initialY1: y1 }; + return { y1, datum, y0: bandedSpec ? y0 : null, mark, initialY0: y0, initialY1: y1 }; } function finiteOrNull(value: unknown, nonNumericValues: unknown[]): number | null { @@ -324,8 +332,8 @@ export function getFormattedDataSeries( ); const fittedAndStackedDataSeries = stackedGroups.reduce((acc, dataSeries) => { - const [{ stackMode }] = dataSeries; - const formatted = formatStackedDataSeriesValues(dataSeries, xValues, stackMode); + const [{ stackMode, seriesType }] = dataSeries; + const formatted = formatStackedDataSeriesValues(dataSeries, xValues, seriesType, stackMode); return [...acc, ...formatted]; }, []); // get already fitted non stacked dataSeries @@ -444,9 +452,12 @@ export function getDataSeriesFromSpecs( }; } -/** @internal */ -export function isDataSeriesBanded({ spec }: DataSeries) { - return spec.y0Accessors && spec.y0Accessors.length > 0; +/** + * TODO: Add check for chart type other than area and bar. + * @internal + */ +export function isBandedSpec(spec: BasicSeriesSpec): boolean { + return Boolean(spec.y0Accessors && spec.y0Accessors.length > 0 && !isStackedSpec(spec, false)); } function getSortedOrdinalXValues( diff --git a/packages/charts/src/chart_types/xy_chart/utils/specs.ts b/packages/charts/src/chart_types/xy_chart/utils/specs.ts index 743b8c52a6..046a6ed09e 100644 --- a/packages/charts/src/chart_types/xy_chart/utils/specs.ts +++ b/packages/charts/src/chart_types/xy_chart/utils/specs.ts @@ -1000,8 +1000,3 @@ export function isLineSeriesSpec(spec: BasicSeriesSpec): spec is LineSeriesSpec export function isAreaSeriesSpec(spec: BasicSeriesSpec): spec is AreaSeriesSpec { return spec.seriesType === SeriesType.Area; } - -/** @internal */ -export function isBandedSpec(y0Accessors: SeriesAccessors['y0Accessors']): boolean { - return Boolean(y0Accessors && y0Accessors.length > 0); -} diff --git a/packages/charts/src/chart_types/xy_chart/utils/stacked_percent_series_utils.test.ts b/packages/charts/src/chart_types/xy_chart/utils/stacked_percent_series_utils.test.ts index 9f5399df70..8f1ae2e205 100644 --- a/packages/charts/src/chart_types/xy_chart/utils/stacked_percent_series_utils.test.ts +++ b/packages/charts/src/chart_types/xy_chart/utils/stacked_percent_series_utils.test.ts @@ -116,7 +116,6 @@ describe('Stacked Series Utils', () => { [ MockSeriesSpec.area({ yAccessors: ['y1'], - y0Accessors: ['y0'], splitSeriesAccessors: ['g'], stackAccessors: ['x'], stackMode: StackMode.Percentage, @@ -128,21 +127,21 @@ describe('Stacked Series Utils', () => { const { formattedDataSeries } = computeSeriesDomainsSelector(store.getState()); const [data0] = formattedDataSeries[0].data; - expect(data0.initialY0).toBe(2); + expect(data0.initialY0).toBeNull(); expect(data0.initialY1).toBe(10); - expect(data0.y0).toBe(0.02); + expect(data0.y0).toBe(0); expect(data0.y1).toBe(0.1); const [data1] = formattedDataSeries[1].data; - expect(data1.initialY0).toBe(4); + expect(data1.initialY0).toBeNull(); expect(data1.initialY1).toBe(20); - expect(data1.y0).toBe(0.14); + expect(data1.y0).toBe(0.1); expect(data1.y1).toBeCloseTo(0.3, 5); const [data2] = formattedDataSeries[2].data; - expect(data2.initialY0).toBe(6); + expect(data2.initialY0).toBeNull(); expect(data2.initialY1).toBe(70); - expect(data2.y0).toBeCloseTo(0.36); + expect(data2.y0).toBeCloseTo(0.3); expect(data2.y1).toBe(1); }); test('format data with nulls - missing points', () => { @@ -151,7 +150,6 @@ describe('Stacked Series Utils', () => { [ MockSeriesSpec.area({ yAccessors: ['y1'], - y0Accessors: ['y0'], splitSeriesAccessors: ['g'], stackAccessors: ['x'], stackMode: StackMode.Percentage, @@ -163,9 +161,9 @@ describe('Stacked Series Utils', () => { const { formattedDataSeries } = computeSeriesDomainsSelector(store.getState()); const [data0] = formattedDataSeries[0].data; - expect(data0.initialY0).toBe(2); + expect(data0.initialY0).toBeNull(); expect(data0.initialY1).toBe(10); - expect(data0.y0).toBe(0.02); + expect(data0.y0).toBe(0); expect(data0.y1).toBe(0.1); const [data1] = formattedDataSeries[1].data; @@ -175,9 +173,9 @@ describe('Stacked Series Utils', () => { expect(data1.y1).toBe(0.1); const [data2] = formattedDataSeries[2].data; - expect(data2.initialY0).toBe(6); + expect(data2.initialY0).toBeNull(); expect(data2.initialY1).toBe(90); - expect(data2.y0).toBe(0.16); + expect(data2.y0).toBe(0.1); expect(data2.y1).toBe(1); }); test('format data without nulls on second series', () => { diff --git a/packages/charts/src/chart_types/xy_chart/utils/stacked_series_utils.test.ts b/packages/charts/src/chart_types/xy_chart/utils/stacked_series_utils.test.ts index 332e5b374b..8cc9adf692 100644 --- a/packages/charts/src/chart_types/xy_chart/utils/stacked_series_utils.test.ts +++ b/packages/charts/src/chart_types/xy_chart/utils/stacked_series_utils.test.ts @@ -141,6 +141,7 @@ describe('Stacked Series Utils', () => { MockStore.addSpecs( MockSeriesSpec.area({ ...WITH_NULL_DATASET, + stackAccessors: ['yes'], stackMode: StackMode.Percentage, }), store, @@ -210,7 +211,7 @@ describe('Stacked Series Utils', () => { initialY0: 2, initialY1: 10, x: 0, - y0: 2, + y0: 0, y1: 10, mark: null, }); @@ -218,7 +219,7 @@ describe('Stacked Series Utils', () => { initialY0: 4, initialY1: 20, x: 0, - y0: 14, + y0: 10, y1: 30, mark: null, }); @@ -226,7 +227,7 @@ describe('Stacked Series Utils', () => { initialY0: 6, initialY1: 30, x: 0, - y0: 36, + y0: 30, y1: 60, mark: null, }); @@ -240,7 +241,7 @@ describe('Stacked Series Utils', () => { initialY0: 2, initialY1: 10, x: 0, - y0: 2, + y0: 0, y1: 10, mark: null, }); @@ -248,15 +249,15 @@ describe('Stacked Series Utils', () => { initialY0: null, initialY1: null, x: 0, - y1: 10, y0: 10, + y1: 10, mark: null, }); expect(formattedDataSeries[2].data[0]).toMatchObject({ initialY0: 6, initialY1: 30, x: 0, - y0: 16, + y0: 10, y1: 40, mark: null, }); diff --git a/packages/charts/src/chart_types/xy_chart/utils/stacked_series_utils.ts b/packages/charts/src/chart_types/xy_chart/utils/stacked_series_utils.ts index b32fb01462..37dfb3fe34 100644 --- a/packages/charts/src/chart_types/xy_chart/utils/stacked_series_utils.ts +++ b/packages/charts/src/chart_types/xy_chart/utils/stacked_series_utils.ts @@ -6,21 +6,22 @@ * Side Public License, v 1. */ -import { - stack as D3Stack, - stackOffsetExpand as D3StackOffsetExpand, - stackOffsetNone as D3StackOffsetNone, - stackOffsetSilhouette as D3StackOffsetSilhouette, - stackOffsetWiggle as D3StackOffsetWiggle, - stackOrderNone, - SeriesPoint, -} from 'd3-shape'; +import { stack as D3Stack, stackOffsetWiggle, stackOrderNone } from 'd3-shape'; import { SeriesKey } from '../../../common/series_id'; import { ScaleType } from '../../../scales/constants'; -import { clamp, isFiniteNumber } from '../../../utils/common'; +import { clamp, isDefined } from '../../../utils/common'; +import { Logger } from '../../../utils/logger'; +import { + diverging, + divergingPercentage, + divergingSilhouette, + divergingWiggle, + XValueMap, + XValueSeriesDatum, +} from './diverging_offsets'; import { DataSeries, DataSeriesDatum } from './series'; -import { StackMode } from './specs'; +import { SeriesType, StackMode } from './specs'; /** @internal */ export interface StackedValues { @@ -40,133 +41,108 @@ export const datumXSortPredicate = (xScaleType: ScaleType, sortedXValues?: (stri return a.x - b.x; }; -type D3StackArrayElement = Record; -type D3UnionStack = Record< - SeriesKey, - { - y0: SeriesPoint[]; - y1: SeriesPoint[]; - } ->; - /** @internal */ export function formatStackedDataSeriesValues( dataSeries: DataSeries[], xValues: Set, + seriesType: SeriesType, stackMode?: StackMode, ): DataSeries[] { - const dataSeriesKeys = dataSeries.reduce>((acc, curr) => { - acc[curr.key] = curr; - return acc; - }, {}); - const stackAsPercentage = stackMode === StackMode.Percentage; - - const xValuesArray = [...xValues]; - const reorderedArray: Array = []; - const xValueMap: Map> = new Map(); - // transforming the current set of series into the d3 stack required data structure - dataSeries.forEach(({ data, key, isFiltered }) => { - if (isFiltered) { - return; - } - const dsMap: Map = new Map(); - data.forEach((d) => { - const { x, y0, y1 } = d; - const xIndex = xValuesArray.indexOf(x); + const dataSeriesMap = dataSeries.reduce>((acc, curr) => { + return acc.set(curr.key, curr); + }, new Map()); + let hasNegative = false; + let hasPositive = false; - if (reorderedArray[xIndex] === undefined) { - reorderedArray[xIndex] = { x }; - } - // y0 can be considered as always present - reorderedArray[xIndex][`${key}-y0`] = isFiniteNumber(y0) ? (stackAsPercentage ? Math.abs(y0) : y0) : y0; - // if y0 is available, we have to count y1 as the different of y1 and y0 - // to correctly stack them when stacking banded charts - const nonNullY1 = y1 ?? 0; - const nonNullY0 = y0 ?? 0; - reorderedArray[xIndex][`${key}-y1`] = - (stackAsPercentage ? Math.abs(nonNullY1) : nonNullY1) - (stackAsPercentage ? Math.abs(nonNullY0) : nonNullY0); - dsMap.set(x, d); + // group data series by x values + const xMap: XValueMap = new Map(); + [...xValues].forEach((xValue) => { + const seriesMap = new Map(); + dataSeries.forEach(({ key, data, isFiltered }) => { + if (isFiltered) return; + const datum = data.find(({ x }) => x === xValue); + if (!datum) return; + const y1 = datum.y1 ?? 0; + if (hasPositive || y1 > 0) hasPositive = true; + if (hasNegative || y1 < 0) hasNegative = true; + seriesMap.set(`${key}-y0`, datum); + seriesMap.set(key, datum); }); - xValueMap.set(key, dsMap); + xMap.set(xValue, seriesMap); }); - const stackOffset = getOffsetBasedOnStackMode(stackMode); + if (hasNegative && hasPositive && seriesType === SeriesType.Area) { + Logger.warn( + `Area series should be avoided with dataset containing positive and negative values. Use a bar series instead.`, + ); + } - const keys = Object.keys(dataSeriesKeys).reduce((acc, key) => [...acc, `${key}-y0`, `${key}-y1`], []); + const keys = [...dataSeriesMap.keys()].reduce((acc, key) => [...acc, `${key}-y0`, key], []); + const stackOffset = getOffsetBasedOnStackMode(stackMode, hasNegative && !hasPositive); + const stack = D3Stack() + .keys(keys) + .value(([, indexMap], key) => { + const datum = indexMap.get(key); + if (!datum) return 0; // hides filtered series while maintaining their existence + return key.endsWith('-y0') ? datum.y0 ?? 0 : datum.y1 ?? 0; + }) + .order(stackOrderNone) + .offset(stackOffset)(xMap) + .filter(({ key }) => !key.endsWith('-y0')); - const stack = D3Stack().keys(keys).order(stackOrderNone).offset(stackOffset)(reorderedArray); + return stack + .map((stackedSeries) => { + const dataSeriesProps = dataSeriesMap.get(stackedSeries.key); + if (!dataSeriesProps) return null; + const data = stackedSeries + .map((row) => { + const d = row.data[1].get(stackedSeries.key); + if (!d || d.x === undefined || d.x === null) return null; + const { initialY0, initialY1, mark, datum, filled, x } = d; + const [y0, y1] = row; - const unionedYStacks = stack.reduce((acc, d) => { - const key = d.key.slice(0, -3); - const accessor = d.key.slice(-2); - if (accessor !== 'y1' && accessor !== 'y0') { - return acc; - } - if (!acc[key]) { - acc[key] = { - y0: [], - y1: [], + return { + x, + /** + * Due to floating point errors, values computed on a stack + * could falls out of the current defined domain boundaries. + * This in particular cause issues with percent stack, where the domain + * is hardcoded to [0,1] and some value can fall outside that domain. + */ + y1: clampIfStackedAsPercentage(y1, stackMode), + y0: clampIfStackedAsPercentage(y0, stackMode), + initialY0, + initialY1, + mark, + datum, + filled, + }; + }) + .filter(isDefined); + return { + ...dataSeriesProps, + data, }; - } - acc[key][accessor] = d.map((da) => da); - return acc; - }, {}); - - return Object.keys(unionedYStacks).map((stackedDataSeriesKey) => { - const dataSeriesProps = dataSeriesKeys[stackedDataSeriesKey]; - const dsMap = xValueMap.get(stackedDataSeriesKey); - const { y0: y0StackArray, y1: y1StackArray } = unionedYStacks[stackedDataSeriesKey]; - const data = y1StackArray - .map((y1Stack, index) => { - const { x } = y1Stack.data; - if (x === undefined || x === null) { - return null; - } - const originalData = dsMap?.get(x); - if (!originalData) { - return null; - } - const [, y0] = y0StackArray[index]; - const [, y1] = y1Stack; - const { initialY0, initialY1, mark, datum, filled } = originalData; - return { - x, - /** - * Due to floating point errors, values computed on a stack - * could falls out of the current defined domain boundaries. - * This in particular cause issues with percent stack, where the domain - * is hardcoded to [0,1] and some value can fall outside that domain. - */ - y1: clampIfStackedAsPercentage(y1, stackMode), - y0: clampIfStackedAsPercentage(y0, stackMode), - initialY0, - initialY1, - mark, - datum, - filled, - }; - }) - .filter((d) => d !== null) as DataSeriesDatum[]; - return { - ...dataSeriesProps, - data, - }; - }); + }) + .filter(isDefined); } function clampIfStackedAsPercentage(value: number, stackMode?: StackMode) { return stackMode === StackMode.Percentage ? clamp(value, 0, 1) : value; } -function getOffsetBasedOnStackMode(stackMode?: StackMode) { +function getOffsetBasedOnStackMode(stackMode?: StackMode, onlyNegative = false) { + // TODO: fix diverging wiggle offset for negative polarity data + if (onlyNegative && stackMode === StackMode.Wiggle) return stackOffsetWiggle; + switch (stackMode) { case StackMode.Percentage: - return D3StackOffsetExpand; + return divergingPercentage; case StackMode.Silhouette: - return D3StackOffsetSilhouette; + return divergingSilhouette; case StackMode.Wiggle: - return D3StackOffsetWiggle; + return divergingWiggle; default: - return D3StackOffsetNone; + return diverging; } } diff --git a/storybook/stories/area/14_stacked_band.story.tsx b/storybook/stories/area/14_stacked_band.story.tsx deleted file mode 100644 index e00439c2b0..0000000000 --- a/storybook/stories/area/14_stacked_band.story.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 React from 'react'; - -import { AreaSeries, Axis, Chart, Position, ScaleType, Settings, timeFormatter } from '@elastic/charts'; -import { KIBANA_METRICS } from '@elastic/charts/src/utils/data_samples/test_dataset_kibana'; - -import { useBaseTheme } from '../../use_base_theme'; - -const dateFormatter = timeFormatter('HH:mm'); - -export const Example = () => { - const { data } = KIBANA_METRICS.metrics.kibana_os_load[0]; - const data2 = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d) => [d[0], 20, 10]); - - return ( - - - - Number(d).toFixed(2)} - /> - - - - - - ); -}; diff --git a/storybook/stories/area/area.stories.tsx b/storybook/stories/area/area.stories.tsx index 1b527abea8..50646bcbc8 100644 --- a/storybook/stories/area/area.stories.tsx +++ b/storybook/stories/area/area.stories.tsx @@ -23,7 +23,6 @@ export { Example as stackedPercentageWithZeros } from './8_stacked_percentage_ze export { Example as stackedSeparateSpecs } from './9_stacked_separate_specs.story'; export { Example as stackedSameNaming } from './10_stacked_same_naming.story'; export { Example as bandArea } from './13_band_area.story'; -export { Example as stackedBand } from './14_stacked_band.story'; export { Example as stackedGrouped } from './15_stacked_grouped.story'; export { Example as withNegativeValues } from './17_negative.story'; export { Example as withNegativeAndPositive } from './18_negative_positive.story'; diff --git a/storybook/stories/mixed/8_polarized_stacked.story.tsx b/storybook/stories/mixed/8_polarized_stacked.story.tsx new file mode 100644 index 0000000000..7d116f6e15 --- /dev/null +++ b/storybook/stories/mixed/8_polarized_stacked.story.tsx @@ -0,0 +1,85 @@ +/* + * 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 { boolean, select } from '@storybook/addon-knobs'; +import numeral from 'numeral'; +import React from 'react'; + +import { + Axis, + Chart, + Position, + ScaleType, + Settings, + StackMode, + SeriesType, + LineAnnotation, + AnnotationDomainType, +} from '@elastic/charts'; + +import { useBaseTheme } from '../../use_base_theme'; +import { data } from '../utils/datasets/product_profits'; +import { getXYSeriesKnob } from '../utils/knobs'; + +export const Example = () => { + const baseTheme = useBaseTheme(); + const stacked = boolean('stacked', true); + const polarity = select('data polarity', ['Mixed', 'Positive', 'Negative'], 'Mixed'); + const customDomain = boolean('custom domain', false); + const stackMode = + select( + 'stackMode', + { + None: undefined, + Silhouette: StackMode.Silhouette, + Wiggle: StackMode.Wiggle, + Percentage: StackMode.Percentage, + }, + undefined, + ) ?? undefined; + const [Series] = getXYSeriesKnob('SeriesType', SeriesType.Bar, undefined, { + ignore: [SeriesType.Bubble, SeriesType.Line], + }); + return ( + + + + numeral(d).format(stackMode === 'percentage' ? '0%' : '$0,0')} + /> + + + + 1]} // for testing warning + yAccessors={[ + ({ profit }) => (polarity === 'Mixed' ? profit : (polarity === 'Negative' ? -1 : 1) * Math.abs(profit)), + ]} + splitSeriesAccessors={['state']} + stackMode={stackMode} + stackAccessors={stacked ? ['yes'] : undefined} + data={data} + /> + + ); +}; diff --git a/storybook/stories/mixed/mixed.stories.tsx b/storybook/stories/mixed/mixed.stories.tsx index a9e318d8c9..65197c2379 100644 --- a/storybook/stories/mixed/mixed.stories.tsx +++ b/storybook/stories/mixed/mixed.stories.tsx @@ -18,3 +18,4 @@ export { Example as testBarLinesTime } from './5_test_bar_time.story'; export { Example as fittingFunctionsNonStackedSeries } from './6_fitting.story'; export { Example as fittingFunctionsStackedSeries } from './6_fitting_stacked.story'; export { Example as markSizeAccessor } from './7_marks.story'; +export { Example as PolarizedStacked } from './8_polarized_stacked.story'; diff --git a/storybook/stories/utils/datasets/product_profits.ts b/storybook/stories/utils/datasets/product_profits.ts new file mode 100644 index 0000000000..d1c8803686 --- /dev/null +++ b/storybook/stories/utils/datasets/product_profits.ts @@ -0,0 +1,56 @@ +/* + * 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. + */ + +interface Row { + state: string; + profit: number; + product: string; +} + +export const data: Row[] = [ + { state: 'Florida', product: 'Powder', profit: 10000 }, + { state: 'Florida', product: 'Mascara', profit: 9000 }, + { state: 'Florida', product: 'Lip gloss', profit: 17000 }, + { state: 'Florida', product: 'Foundation', profit: 6000 }, + { state: 'Florida', product: 'Eyeliner', profit: 7000 }, + { state: 'Florida', product: 'Eyeshadow', profit: 1000 }, + { state: 'Florida', product: 'Lipstick', profit: 4000 }, + { state: 'Florida', product: 'Rouge', profit: 6000 }, + { state: 'Florida', product: 'Concealer', profit: 8000 }, + { state: 'Florida', product: 'Nail polish', profit: 8000 }, + { state: 'Texas', product: 'Powder', profit: 9000 }, + { state: 'Texas', product: 'Mascara', profit: 7000 }, + { state: 'Texas', product: 'Lip gloss', profit: 8000 }, + { state: 'Texas', product: 'Foundation', profit: 6000 }, + { state: 'Texas', product: 'Eyeliner', profit: 10000 }, + { state: 'Texas', product: 'Eyeshadow', profit: 9000 }, + { state: 'Texas', product: 'Lipstick', profit: 5000 }, + { state: 'Texas', product: 'Rouge', profit: -4000 }, + { state: 'Texas', product: 'Concealer', profit: -3000 }, + { state: 'Texas', product: 'Nail polish', profit: -2000 }, + { state: 'Nevada', product: 'Powder', profit: -5000 }, + { state: 'Nevada', product: 'Mascara', profit: -4000 }, + { state: 'Nevada', product: 'Lip gloss', profit: 2000 }, + { state: 'Nevada', product: 'Foundation', profit: -2000 }, + { state: 'Nevada', product: 'Eyeliner', profit: 1000 }, + { state: 'Nevada', product: 'Eyeshadow', profit: -2000 }, + { state: 'Nevada', product: 'Lipstick', profit: 1000 }, + { state: 'Nevada', product: 'Rouge', profit: -1000 }, + { state: 'Nevada', product: 'Concealer', profit: -1000 }, + { state: 'Nevada', product: 'Nail polish', profit: -1000 }, + { state: 'Arizona', product: 'Powder', profit: 20000 }, + { state: 'Arizona', product: 'Mascara', profit: 21000 }, + { state: 'Arizona', product: 'Lip gloss', profit: -1000 }, + { state: 'Arizona', product: 'Foundation', profit: 8000 }, + { state: 'Arizona', product: 'Eyeliner', profit: -2000 }, + { state: 'Arizona', product: 'Eyeshadow', profit: -1000 }, + { state: 'Arizona', product: 'Lipstick', profit: -4000 }, + { state: 'Arizona', product: 'Rouge', profit: 2000 }, + { state: 'Arizona', product: 'Concealer', profit: -1000 }, + { state: 'Arizona', product: 'Nail polish', profit: -1000 }, +]; diff --git a/yarn.lock b/yarn.lock index 336c25cf22..a1a570e809 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5609,10 +5609,10 @@ dependencies: "@types/d3-color" "*" -"@types/d3-path@*": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.8.tgz#48e6945a8ff43ee0a1ce85c8cfa2337de85c7c79" - integrity sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA== +"@types/d3-path@^2": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-2.0.1.tgz#ca03dfa8b94d8add97ad0cd97e96e2006b4763cb" + integrity sha512-6K8LaFlztlhZO7mwsZg7ClRsdLg3FJRzIIi6SZXDWmmSJc2x8dd2VkESbLXdk3p8cuvz71f36S0y8Zv2AxqvQw== "@types/d3-scale@^2.1.1": version "2.1.1" @@ -5621,12 +5621,12 @@ dependencies: "@types/d3-time" "*" -"@types/d3-shape@^1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.1.tgz#1b4f92b7efd7306fe2474dc6ee94c0f0ed2e6ab6" - integrity sha512-usqdvUvPJ7AJNwpd2drOzRKs1ELie53p2m2GnPKr076/ADM579jVTJ5dPsoZ5E/CMNWk8lvPWYQSvilpp6jjwg== +"@types/d3-shape@^2.0.0": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-2.1.3.tgz#35d397b9e687abaa0de82343b250b9897b8cacf3" + integrity sha512-HAhCel3wP93kh4/rq+7atLdybcESZ5bRHDEZUojClyZWsRuEMo3A52NGYJSh48SxfxEU6RZIVbZL2YFZ2OAlzQ== dependencies: - "@types/d3-path" "*" + "@types/d3-path" "^2" "@types/d3-time@*": version "1.0.10" @@ -9373,11 +9373,16 @@ d3-collection@1, d3-collection@^1.0.7: resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e" integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A== -d3-color@1, d3-color@^1.4.0: +d3-color@1: version "1.4.0" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.0.tgz#89c45a995ed773b13314f06460df26d60ba0ecaf" integrity sha512-TzNPeJy2+iEepfiL92LAAB7fvnp/dV2YwANPVHdDWmYMm23qIJBYww3qT8I8C1wXrmrg4UWs7BKc2tKIgyjzHg== +d3-color@^1.4.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a" + integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== + d3-dispatch@^1.0.3: version "1.0.6" resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz#00d37bcee4dd8cd97729dd893a0ac29caaba5d58" @@ -9403,9 +9408,14 @@ d3-interpolate@^1.4.0: d3-color "1" d3-path@1: - version "1.0.8" - resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.8.tgz#4a0606a794d104513ec4a8af43525f374b278719" - integrity sha512-J6EfUNwcMQ+aM5YPOB8ZbgAZu6wc82f/0WFxrxwV6Ll8wBwLaHLKCqQ5Imub02JriCVVdPjgI+6P3a4EWJCxAg== + version "1.0.9" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf" + integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== + +"d3-path@1 - 2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-2.0.0.tgz#55d86ac131a0548adae241eebfb56b4582dd09d8" + integrity sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA== d3-scale@^1.0.7: version "1.0.7" @@ -9427,6 +9437,13 @@ d3-shape@^1.3.4: dependencies: d3-path "1" +d3-shape@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-2.1.0.tgz#3b6a82ccafbc45de55b57fcf956c584ded3b666f" + integrity sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA== + dependencies: + d3-path "1 - 2" + d3-time-format@2: version "2.1.3" resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.3.tgz#ae06f8e0126a9d60d6364eac5b1533ae1bac826b" @@ -10612,7 +10629,8 @@ eslint-module-utils@^2.6.0: pkg-dir "^2.0.0" "eslint-plugin-elastic-charts@link:./packages/eslint-plugin-elastic-charts": - version "1.0.0" + version "0.0.0" + uid "" eslint-plugin-eslint-comments@^3.2.0: version "3.2.0" @@ -15016,13 +15034,8 @@ lines-and-columns@^1.1.6: integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= "link-kibana@link:./packages/link_kibana": - version "1.0.0" - dependencies: - chalk "^4.1.1" - change-case "^4.1.2" - glob "^7.1.7" - inquirer "^8.0.0" - ora "^5.4.0" + version "0.0.0" + uid "" lint-staged@^10.5.3: version "10.5.3"