@automattic/charts 1.10.2 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/TOKENS.md +72 -0
  3. package/dist/index.cjs +149 -44
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.css +60 -47
  6. package/dist/index.d.cts +17 -7
  7. package/dist/index.d.ts +17 -7
  8. package/dist/index.js +150 -45
  9. package/dist/index.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss +7 -7
  12. package/src/charts/heatmap-chart/heatmap-chart.module.scss +18 -13
  13. package/src/charts/heatmap-chart/heatmap-chart.tsx +11 -7
  14. package/src/charts/heatmap-chart/private/heatmap-legend.tsx +3 -3
  15. package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +13 -7
  16. package/src/charts/leaderboard-chart/hooks/index.ts +1 -0
  17. package/src/charts/leaderboard-chart/hooks/use-fitted-row-count.ts +115 -0
  18. package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +47 -20
  19. package/src/charts/leaderboard-chart/leaderboard-chart.tsx +72 -16
  20. package/src/charts/leaderboard-chart/test/leaderboard-chart-fit-rows.test.tsx +365 -0
  21. package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +73 -2
  22. package/src/charts/leaderboard-chart/types.ts +12 -0
  23. package/src/charts/line-chart/line-chart.module.scss +2 -2
  24. package/src/charts/private/grid-control/grid-control.module.scss +1 -1
  25. package/src/charts/private/svg-empty-state/svg-empty-state.module.scss +1 -1
  26. package/src/charts/private/x-zoom.module.scss +12 -10
  27. package/src/components/tooltip/base-tooltip.module.scss +3 -2
  28. package/src/components/trend-indicator/trend-indicator.module.scss +6 -5
  29. package/src/providers/chart-context/themes.ts +33 -19
  30. package/src/types.ts +2 -2
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { AnimatedAreaSeries, AnimatedAreaStack, AreaSeries, Axis, BarGroup, BarS
4
4
  import { __, sprintf } from "@wordpress/i18n";
5
5
  import clsx from "clsx";
6
6
  import * as React from "react";
7
- import { Children, Fragment, Fragment as Fragment$2, createContext, createContext as createContext$1, createElement, forwardRef, forwardRef as forwardRef$1, isValidElement, memo, useCallback, useCallback as useCallback$1, useContext, useContext as useContext$1, useEffect, useEffect as useEffect$1, useId, useImperativeHandle, useLayoutEffect, useMemo, useMemo as useMemo$1, useRef, useRef as useRef$1, useState, useState as useState$1 } from "react";
7
+ import { Children, Fragment, createContext, createContext as createContext$1, createElement, forwardRef, forwardRef as forwardRef$1, isValidElement, memo, useCallback, useCallback as useCallback$1, useContext, useContext as useContext$1, useEffect, useEffect as useEffect$1, useId, useImperativeHandle, useLayoutEffect, useMemo, useMemo as useMemo$1, useRef, useRef as useRef$1, useState, useState as useState$1 } from "react";
8
8
  import { color, hsl } from "@visx/vendor/d3-color";
9
9
  import { addDays, differenceInCalendarWeeks, differenceInHours, differenceInYears, format, isValid, parse, parseISO, startOfWeek } from "date-fns";
10
10
  import { Text, getStringWidth } from "@visx/text";
@@ -722,9 +722,9 @@ const getChartColor = (index, colorCache) => {
722
722
  * Default theme configuration
723
723
  */
724
724
  const defaultTheme = {
725
- backgroundColor: "var(--wpds-color-background-surface-neutral-strong, #fff)",
725
+ backgroundColor: "var(--a8c-charts-color-background, var(--wpds-color-background-surface-neutral-strong, #fff))",
726
726
  labelBackgroundColor: "transparent",
727
- labelTextColor: "#FFFFFF",
727
+ labelTextColor: "var(--a8c-charts-color-label-on-fill, #FFFFFF)",
728
728
  colors: [
729
729
  "#98C8DF",
730
730
  "#006DAB",
@@ -733,59 +733,59 @@ const defaultTheme = {
733
733
  "#FF8C8F"
734
734
  ],
735
735
  gridStyles: {
736
- stroke: "var(--wpds-color-stroke-surface-neutral, #dbdbdb)",
736
+ stroke: "var(--a8c-charts-color-grid, var(--wpds-color-stroke-surface-neutral, #dbdbdb))",
737
737
  strokeWidth: 1
738
738
  },
739
739
  tickLength: 4,
740
740
  gridColor: "",
741
741
  gridColorDark: "",
742
742
  xTickLineStyles: {
743
- stroke: "var(--wpds-color-stroke-surface-neutral, #dbdbdb)",
743
+ stroke: "var(--a8c-charts-color-tick, var(--wpds-color-stroke-surface-neutral, #dbdbdb))",
744
744
  strokeWidth: 1
745
745
  },
746
746
  xAxisLineStyles: {
747
- stroke: "var(--wpds-color-stroke-surface-neutral, #dbdbdb)",
747
+ stroke: "var(--a8c-charts-color-axis, var(--wpds-color-stroke-surface-neutral, #dbdbdb))",
748
748
  strokeWidth: 1
749
749
  },
750
750
  legend: {
751
- labelStyles: { color: "var(--wpds-color-foreground-content-neutral, #1e1e1e)" },
751
+ labelStyles: { color: "var(--a8c-charts-color-label, var(--wpds-color-foreground-content-neutral, #1e1e1e))" },
752
752
  containerStyles: {},
753
753
  shapeStyles: []
754
754
  },
755
755
  seriesLineStyles: [],
756
756
  glyphs: [],
757
757
  svgLabelSmall: {
758
- fill: "var(--wpds-color-foreground-content-neutral, #1e1e1e)",
758
+ fill: "var(--a8c-charts-color-label, var(--wpds-color-foreground-content-neutral, #1e1e1e))",
759
759
  fontFamily: "inherit"
760
760
  },
761
761
  svgLabelBig: { fontFamily: "inherit" },
762
762
  annotationStyles: {
763
763
  label: {
764
- anchorLineStroke: "var(--wpds-color-foreground-content-neutral, #1e1e1e)",
765
- backgroundFill: "var(--wpds-color-background-surface-neutral-strong, #fff)"
764
+ anchorLineStroke: "var(--a8c-charts-color-annotation, var(--wpds-color-foreground-content-neutral, #1e1e1e))",
765
+ backgroundFill: "var(--a8c-charts-color-background, var(--wpds-color-background-surface-neutral-strong, #fff))"
766
766
  },
767
- connector: { stroke: "var(--wpds-color-foreground-content-neutral, #1e1e1e)" },
767
+ connector: { stroke: "var(--a8c-charts-color-annotation, var(--wpds-color-foreground-content-neutral, #1e1e1e))" },
768
768
  circleSubject: {
769
769
  stroke: "transparent",
770
- fill: "var(--wpds-color-foreground-content-neutral, #1e1e1e)",
770
+ fill: "var(--a8c-charts-color-annotation, var(--wpds-color-foreground-content-neutral, #1e1e1e))",
771
771
  radius: 5
772
772
  }
773
773
  },
774
- geoChart: { featureFillColor: "var(--wpds-color-background-surface-neutral-weak, #f4f4f4)" },
774
+ geoChart: { featureFillColor: "var(--a8c-charts-color-surface-secondary, var(--wpds-color-background-surface-neutral-weak, #f4f4f4))" },
775
775
  leaderboardChart: {
776
776
  rowGap: 12,
777
777
  columnGap: 4,
778
778
  labelSpacing: "xs",
779
779
  deltaColors: [
780
- "var(--wpds-color-foreground-content-error-weak, #cc1818)",
781
- "var(--wpds-color-foreground-content-neutral-weak, #707070)",
782
- "var(--wpds-color-foreground-content-success-weak, #008030)"
780
+ "var(--a8c-charts-color-trend-down, var(--wpds-color-foreground-content-error-weak, #cc1818))",
781
+ "var(--a8c-charts-color-trend-neutral, var(--wpds-color-foreground-content-neutral-weak, #707070))",
782
+ "var(--a8c-charts-color-trend-up, var(--wpds-color-foreground-content-success-weak, #008030))"
783
783
  ]
784
784
  },
785
785
  conversionFunnelChart: {
786
- backgroundColor: "var(--wpds-color-background-surface-neutral-weak, #f4f4f4)",
787
- positiveChangeColor: "var(--wpds-color-foreground-content-success-weak, #008030)",
788
- negativeChangeColor: "var(--wpds-color-foreground-content-error-weak, #cc1818)"
786
+ backgroundColor: "var(--a8c-charts-color-surface-secondary, var(--wpds-color-background-surface-neutral-weak, #f4f4f4))",
787
+ positiveChangeColor: "var(--a8c-charts-color-trend-up, var(--wpds-color-foreground-content-success-weak, #008030))",
788
+ negativeChangeColor: "var(--a8c-charts-color-trend-down, var(--wpds-color-foreground-content-error-weak, #cc1818))"
789
789
  },
790
790
  lineChart: { lineStyles: { comparison: {
791
791
  strokeDasharray: "4 4",
@@ -6128,9 +6128,9 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
6128
6128
  "aria-hidden": "true",
6129
6129
  className: heatmap_chart_module_default["heatmap-chart__legend-swatch"],
6130
6130
  style: {
6131
- "--heatmap-primary": primaryColorHex,
6132
- "--heatmap-bg": backgroundColor,
6133
- "--intensity": intensity
6131
+ "--a8c-charts-color-heatmap-primary": primaryColorHex,
6132
+ "--a8c-charts-color-heatmap-background": backgroundColor,
6133
+ "--a8c-charts-heatmap-cell-intensity": intensity
6134
6134
  }
6135
6135
  }, index);
6136
6136
  })
@@ -6288,17 +6288,17 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6288
6288
  children: __("No data available", "jetpack-charts")
6289
6289
  })
6290
6290
  });
6291
- const columnTrack = compact ? "var(--heatmap-cell-size)" : `minmax(${minCellWidth ?? 0}px, ${maxCellWidth ? `${maxCellWidth}px` : "1fr"})`;
6292
- const rowTrack = compact ? "var(--heatmap-cell-size)" : `minmax(${minCellHeight ?? 0}px, ${maxCellHeight ? `${maxCellHeight}px` : "1fr"})`;
6291
+ const columnTrack = compact ? "var(--a8c-charts-dimension-heatmap-cell-size)" : `minmax(${minCellWidth ?? 0}px, ${maxCellWidth ? `${maxCellWidth}px` : "1fr"})`;
6292
+ const rowTrack = compact ? "var(--a8c-charts-dimension-heatmap-cell-size)" : `minmax(${minCellHeight ?? 0}px, ${maxCellHeight ? `${maxCellHeight}px` : "1fr"})`;
6293
6293
  const gridStyle = {
6294
- "--heatmap-primary": primaryColorHex,
6295
- "--heatmap-bg": theme.backgroundColor,
6294
+ "--a8c-charts-color-heatmap-primary": primaryColorHex,
6295
+ "--a8c-charts-color-heatmap-background": theme.backgroundColor,
6296
6296
  gridTemplateColumns: `auto repeat(${columns}, ${columnTrack})`,
6297
6297
  gridTemplateRows: `auto repeat(${rows}, ${rowTrack})`
6298
6298
  };
6299
6299
  if (compact) {
6300
- gridStyle["--heatmap-cell-gap"] = `${compactCellGap}px`;
6301
- gridStyle["--heatmap-cell-size"] = `${compactCellSize}px`;
6300
+ gridStyle["--a8c-charts-dimension-heatmap-cell-gap"] = `${compactCellGap}px`;
6301
+ gridStyle["--a8c-charts-dimension-heatmap-cell-size"] = `${compactCellSize}px`;
6302
6302
  }
6303
6303
  const activeDescendant = selectedIndex !== void 0 ? `${chartId}-cell-${Math.floor(selectedIndex / rows)}-${selectedIndex % rows}` : void 0;
6304
6304
  const heightCapped = !compact && Boolean(maxCellHeight);
@@ -6375,7 +6375,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
6375
6375
  [heatmap_chart_module_default["heatmap-chart__cell--strong"]]: present && cellHasLightText(normalized),
6376
6376
  [heatmap_chart_module_default["heatmap-chart__cell--selected"]]: selectedIndex === flatIndex
6377
6377
  }),
6378
- style: present ? { "--intensity": normalized } : void 0,
6378
+ style: present ? { "--a8c-charts-heatmap-cell-intensity": normalized } : void 0,
6379
6379
  onMouseMove: handleCellMouseMove,
6380
6380
  onMouseLeave: handleCellMouseLeave,
6381
6381
  children: drawValues && present && /* @__PURE__ */ jsx("span", {
@@ -8639,6 +8639,90 @@ function UnconnectedGrid(props, forwardedRef) {
8639
8639
  }
8640
8640
  var component_default = contextConnect(UnconnectedGrid, "Grid");
8641
8641
  //#endregion
8642
+ //#region src/charts/leaderboard-chart/hooks/use-fitted-row-count.ts
8643
+ /**
8644
+ * Fractional layout rounding can put a row's bottom a hair past the container's
8645
+ * without any visible clipping. Allow that much slack before hiding a row.
8646
+ *
8647
+ * Exported so tests and stories assert against the real tolerance rather than
8648
+ * restating it.
8649
+ */
8650
+ const SUBPIXEL_TOLERANCE = .5;
8651
+ /**
8652
+ * Counts how many leading rows fit inside the content container.
8653
+ *
8654
+ * Rows are read from the DOM rather than derived from constants: row height
8655
+ * depends on the theme's row gap, label wrapping, and whatever a caller renders
8656
+ * as a label, so any restated number would drift.
8657
+ *
8658
+ * Every row is a direct grid child marked with `data-row-index`; interactive rows
8659
+ * use a button wrapper and non-interactive rows use a div wrapper.
8660
+ *
8661
+ * @param enabled - Whether to measure at all. When false every row fits.
8662
+ * @param rowCount - Total number of rows rendered.
8663
+ * @param data - Rendered row data, whose geometry may change without changing the count.
8664
+ * @return Ref for the content container and the number of leading rows that fit.
8665
+ */
8666
+ function useFittedRowCount(enabled, rowCount, data) {
8667
+ const contentRef = useRef(null);
8668
+ const [fittedCount, setFittedCount] = useState(rowCount);
8669
+ const [isMeasurable, setIsMeasurable] = useState(true);
8670
+ const measure = useCallback(() => {
8671
+ const content = contentRef.current;
8672
+ if (!content) return;
8673
+ content.scrollTop = 0;
8674
+ const rows = content.querySelectorAll(":scope > [data-leaderboard-grid] > [data-row-index]");
8675
+ const rowBottoms = [];
8676
+ rows.forEach((row) => {
8677
+ const index = Number(row.getAttribute("data-row-index"));
8678
+ if (!Number.isInteger(index) || index < 0 || index >= rowCount) return;
8679
+ const { bottom } = row.getBoundingClientRect();
8680
+ rowBottoms[index] = Math.max(rowBottoms[index] ?? -Infinity, bottom);
8681
+ });
8682
+ if (rowBottoms.length === 0) {
8683
+ setIsMeasurable(false);
8684
+ setFittedCount(rowCount);
8685
+ return;
8686
+ }
8687
+ setIsMeasurable(true);
8688
+ const contentBottom = content.getBoundingClientRect().bottom + SUBPIXEL_TOLERANCE;
8689
+ let fits = 0;
8690
+ while (fits < rowBottoms.length && rowBottoms[fits] <= contentBottom) fits++;
8691
+ setFittedCount((current) => current === fits ? current : fits);
8692
+ }, [rowCount]);
8693
+ useLayoutEffect(() => {
8694
+ if (!enabled) {
8695
+ setFittedCount(rowCount);
8696
+ return;
8697
+ }
8698
+ measure();
8699
+ }, [
8700
+ enabled,
8701
+ rowCount,
8702
+ data,
8703
+ measure
8704
+ ]);
8705
+ useLayoutEffect(() => {
8706
+ if (!enabled) return;
8707
+ const content = contentRef.current;
8708
+ if (!content) return;
8709
+ const observer = new ResizeObserver(measure);
8710
+ observer.observe(content);
8711
+ const grid = content.querySelector(":scope > [data-leaderboard-grid]");
8712
+ if (grid) observer.observe(grid);
8713
+ return () => observer.disconnect();
8714
+ }, [
8715
+ enabled,
8716
+ rowCount,
8717
+ measure
8718
+ ]);
8719
+ return {
8720
+ contentRef,
8721
+ fittedCount: enabled ? fittedCount : rowCount,
8722
+ isMeasurable
8723
+ };
8724
+ }
8725
+ //#endregion
8642
8726
  //#region src/charts/leaderboard-chart/hooks/use-leaderboard-legend-items.ts
8643
8727
  /**
8644
8728
  * Hook to create legend items from leaderboard data
@@ -8699,15 +8783,18 @@ var leaderboard_chart_module_default = {
8699
8783
  "deltaPlaceholder": "a8ccharts-GovfoW-deltaPlaceholder",
8700
8784
  "deltaValue": "a8ccharts-GovfoW-deltaValue",
8701
8785
  "emptyState": "a8ccharts-GovfoW-emptyState",
8786
+ "fitEmptyState": "a8ccharts-GovfoW-fitEmptyState",
8702
8787
  "interactiveRow": "a8ccharts-GovfoW-interactiveRow",
8703
8788
  "is-overlay": "a8ccharts-GovfoW-is-overlay",
8704
8789
  "label": "a8ccharts-GovfoW-label",
8705
8790
  "leaderboardChart": "a8ccharts-GovfoW-leaderboardChart",
8706
8791
  "leaderboardChart__content": "a8ccharts-GovfoW-leaderboardChart__content",
8792
+ "leaderboardChart__content--fit": "a8ccharts-GovfoW-leaderboardChart__content--fit",
8707
8793
  "leaderboardChart--loading": "a8ccharts-GovfoW-leaderboardChart--loading",
8708
8794
  "leaderboardChart--responsive": "a8ccharts-GovfoW-leaderboardChart--responsive",
8709
8795
  "overlap": "a8ccharts-GovfoW-overlap",
8710
8796
  "overlayLabel": "a8ccharts-GovfoW-overlayLabel",
8797
+ "row": "a8ccharts-GovfoW-row",
8711
8798
  "stretch": "a8ccharts-GovfoW-stretch",
8712
8799
  "valueContainer": "a8ccharts-GovfoW-valueContainer"
8713
8800
  };
@@ -8747,8 +8834,9 @@ const defaultDeltaFormatter = (value) => {
8747
8834
  * @param share - The bar's share of the row width, as a percentage.
8748
8835
  * @return A CSS width value.
8749
8836
  */
8750
- const getBarWidth = (share) => `calc(${share}% - var(--a8c--charts--leaderboard--bar--hover-inset, 0px) * ${share} / 100)`;
8751
- const hasComparisonValue = (entry) => entry.previousValue != null && entry.previousShare != null && entry.delta != null;
8837
+ const getBarWidth = (share) => `calc(${share}% - var(--a8c-charts-dimension-leaderboard-bar-hover-inset, 0px) * ${share} / 100)`;
8838
+ const hasPreviousValue = (entry) => entry.previousValue != null && entry.previousShare != null;
8839
+ const hasDeltaValue = (entry) => hasPreviousValue(entry) && entry.delta != null;
8752
8840
  const BarLabel = ({ label }) => /* @__PURE__ */ jsx(Fragment$1, { children: typeof label === "string" ? /* @__PURE__ */ jsx(Text$1, {
8753
8841
  className: leaderboard_chart_module_default.label,
8754
8842
  children: label
@@ -8766,7 +8854,7 @@ const BarWithLabel = ({ entry, withComparison, withOverlayLabel, primaryColor, s
8766
8854
  backgroundColor: primaryColor
8767
8855
  }
8768
8856
  }),
8769
- showComparisonBar && hasComparisonValue(entry) && /* @__PURE__ */ jsx("div", {
8857
+ showComparisonBar && hasPreviousValue(entry) && /* @__PURE__ */ jsx("div", {
8770
8858
  className: clsx(leaderboard_chart_module_default.bar, { [leaderboard_chart_module_default["bar--animated"]]: animation }),
8771
8859
  style: {
8772
8860
  width: getBarWidth(entry.previousShare),
@@ -8792,6 +8880,7 @@ const BarWithLabel = ({ entry, withComparison, withOverlayLabel, primaryColor, s
8792
8880
  * @param props.valueFormatter - Custom formatter for values
8793
8881
  * @param props.deltaFormatter - Custom formatter for delta values
8794
8882
  * @param props.loading - Whether the chart is in loading state
8883
+ * @param props.fitRows - Whether to show only the rows that fit the chart's height
8795
8884
  * @param props.animation - Whether the chart should animate on load
8796
8885
  * @param props.showLegend - Whether to show legend
8797
8886
  * @param props.legend - Legend configuration (orientation, position, alignment, shape, shapeStyles, interactive)
@@ -8802,7 +8891,7 @@ const BarWithLabel = ({ entry, withComparison, withOverlayLabel, primaryColor, s
8802
8891
  * @param props.style - Custom styling for the chart container
8803
8892
  * @return JSX element representing the leaderboard chart
8804
8893
  */
8805
- const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propWidth, height: propHeight, withComparison = false, withOverlayLabel = false, primaryColor, secondaryColor, valueFormatter = defaultValueFormatter, deltaFormatter = defaultDeltaFormatter, animation, loading = false, showLegend = false, legend = {}, legendLabels, gap = "md", className, style, children }) => {
8894
+ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propWidth, height: propHeight, withComparison = false, withOverlayLabel = false, primaryColor, secondaryColor, valueFormatter = defaultValueFormatter, deltaFormatter = defaultDeltaFormatter, animation, loading = false, fitRows = false, showLegend = false, legend = {}, legendLabels, gap = "md", className, style, children }) => {
8806
8895
  const legendInteractive = legend.interactive ?? false;
8807
8896
  const legendPosition = legend.position ?? "bottom";
8808
8897
  const chartId = useChartId(providedChartId);
@@ -8871,6 +8960,8 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
8871
8960
  }), [withComparison, withOverlayLabel])
8872
8961
  });
8873
8962
  const prefersReducedMotion = usePrefersReducedMotion();
8963
+ const { contentRef, fittedCount, isMeasurable } = useFittedRowCount(fitRows && !allSeriesHidden, data?.length ?? 0, data);
8964
+ const shouldFitRows = fitRows && isMeasurable;
8874
8965
  if (!data || data.length === 0) return /* @__PURE__ */ jsx(SingleChartContext.Provider, {
8875
8966
  value: { chartId },
8876
8967
  children: /* @__PURE__ */ jsx(ChartLayout, {
@@ -8923,20 +9014,27 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
8923
9014
  height: propHeight || void 0
8924
9015
  },
8925
9016
  trailingContent: nonLegendChildren,
8926
- children: /* @__PURE__ */ jsx("div", {
8927
- className: leaderboard_chart_module_default.leaderboardChart__content,
8928
- children: allSeriesHidden ? /* @__PURE__ */ jsx("div", {
9017
+ children: /* @__PURE__ */ jsxs("div", {
9018
+ ref: contentRef,
9019
+ className: clsx(leaderboard_chart_module_default.leaderboardChart__content, { [leaderboard_chart_module_default["leaderboardChart__content--fit"]]: shouldFitRows }),
9020
+ children: [shouldFitRows && fittedCount === 0 && !allSeriesHidden && /* @__PURE__ */ jsx("div", {
9021
+ className: clsx(leaderboard_chart_module_default.emptyState, leaderboard_chart_module_default.fitEmptyState),
9022
+ children: __("Not enough space to display data", "jetpack-charts")
9023
+ }), allSeriesHidden ? /* @__PURE__ */ jsx("div", {
8929
9024
  className: leaderboard_chart_module_default.emptyState,
8930
9025
  children: __("All series are hidden. Click legend items to show data.", "jetpack-charts")
8931
9026
  }) : /* @__PURE__ */ jsx(component_default, {
8932
9027
  templateColumns: "minmax(0, 1fr) auto",
8933
9028
  rowGap,
8934
9029
  columnGap,
8935
- children: data.map((entry) => {
9030
+ "data-leaderboard-grid": true,
9031
+ children: data.map((entry, rowIndex) => {
9032
+ const rowStyle = shouldFitRows && rowIndex >= fittedCount ? { visibility: "hidden" } : void 0;
8936
9033
  const showComparisonColumn = withComparison && isComparisonVisible;
8937
- const hasDeltaValue = hasComparisonValue(entry);
8938
- const showComparisonValue = showComparisonColumn && hasDeltaValue;
8939
- const showComparisonPlaceholder = showComparisonColumn && !hasDeltaValue;
9034
+ const hasPreviousPeriodValue = hasPreviousValue(entry);
9035
+ const hasDelta = hasDeltaValue(entry);
9036
+ const showComparisonValue = showComparisonColumn && hasDelta;
9037
+ const showComparisonPlaceholder = showComparisonColumn && !hasDelta;
8940
9038
  const colorIndex = showComparisonValue ? Math.sign(entry.delta) + 1 : 1;
8941
9039
  const deltaColor = deltaColors[colorIndex];
8942
9040
  const rowCells = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Stack, {
@@ -8968,17 +9066,19 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
8968
9066
  style: { color: deltaColor },
8969
9067
  children: [/* @__PURE__ */ jsx("span", {
8970
9068
  "aria-hidden": "true",
8971
- children: "-"
9069
+ children: "—"
8972
9070
  }), /* @__PURE__ */ jsx(component_default$1, {
8973
9071
  as: "span",
8974
- children: __("No comparison data", "jetpack-charts")
9072
+ children: hasPreviousPeriodValue ? __("Percentage change unavailable", "jetpack-charts") : __("No comparison data", "jetpack-charts")
8975
9073
  })]
8976
9074
  })
8977
9075
  ]
8978
9076
  })] });
8979
9077
  if (entry.onClick) return /* @__PURE__ */ jsxs("button", {
8980
9078
  type: "button",
8981
- className: leaderboard_chart_module_default.interactiveRow,
9079
+ "data-row-index": rowIndex,
9080
+ style: rowStyle,
9081
+ className: clsx(leaderboard_chart_module_default.row, leaderboard_chart_module_default.interactiveRow),
8982
9082
  onClick: entry.onClick,
8983
9083
  "aria-label": entry.ariaLabel,
8984
9084
  children: [rowCells, /* @__PURE__ */ jsx(Icon, {
@@ -8987,9 +9087,14 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
8987
9087
  size: 24
8988
9088
  })]
8989
9089
  }, entry.id);
8990
- return /* @__PURE__ */ jsx(Fragment$2, { children: rowCells }, entry.id);
9090
+ return /* @__PURE__ */ jsx("div", {
9091
+ "data-row-index": rowIndex,
9092
+ style: rowStyle,
9093
+ className: leaderboard_chart_module_default.row,
9094
+ children: rowCells
9095
+ }, entry.id);
8991
9096
  })
8992
- })
9097
+ })]
8993
9098
  })
8994
9099
  })
8995
9100
  });