@sproutsocial/seeds-react-data-viz 0.3.1 → 0.4.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.
package/README.md CHANGED
@@ -12,6 +12,8 @@ This package represents the code pillar of Seeds Data Viz, which contains compon
12
12
 
13
13
  - [LineChart](https://github.com/sproutsocial/web-app-core/blob/690405dfd1739511a36b45813c13dfe0da63e662/packages/data-viz/src/components/LineChart/README.md)
14
14
 
15
+ - [VerticalBarChart](./src/components/VerticalBarChart/README.md)
16
+
15
17
  ### Accessories
16
18
 
17
19
  - [ChartLegend](https://github.com/sproutsocial/web-app-core/blob/b06c89ca41ab625848e5068397de11399b2cb992/packages/data-viz/src/components/ChartLegend/README.md)
package/dist/esm/index.js CHANGED
@@ -538,7 +538,12 @@ var ChartXAnnotationMarkerPortal = memo13(
538
538
  );
539
539
 
540
540
  // src/helpers/transformDataToSeries.ts
541
- var transformDataToSeries = ({ data }, type) => {
541
+ var transformDataToSeries = ({
542
+ data
543
+ }, type) => {
544
+ const hasCategoricalData = data.some(
545
+ (series) => series.points.some((point) => typeof point.x === "string")
546
+ );
542
547
  return data.map((dataItem, dataIndex) => {
543
548
  const points = dataItem.points || [];
544
549
  const dataPoints = points.map((point, pointsIndex) => {
@@ -555,12 +560,14 @@ var transformDataToSeries = ({ data }, type) => {
555
560
  enableMarker = true;
556
561
  }
557
562
  return {
558
- x: point.x,
563
+ x: hasCategoricalData ? pointsIndex : point.x,
559
564
  y: point.y,
560
565
  marker: {
561
566
  enabled: enableMarker ? enableMarker : void 0,
562
567
  symbol: enableMarker ? "circle" : void 0
563
- }
568
+ },
569
+ // For categorical data, store the original category name
570
+ ...hasCategoricalData && { name: point.x }
564
571
  };
565
572
  });
566
573
  return {
@@ -590,6 +597,42 @@ var transformTimeSeriesTooltipData = ({
590
597
  });
591
598
  };
592
599
 
600
+ // src/helpers/yAxisLabelFormatter.ts
601
+ import { formatDuration } from "@sproutsocial/seeds-react-duration";
602
+ import { formatNumeral } from "@sproutsocial/seeds-react-numeral";
603
+ var yAxisLabelFormatter = ({
604
+ numberLocale,
605
+ textLocale,
606
+ tickPositions,
607
+ value,
608
+ currency = "USD",
609
+ numberFormat = "decimal"
610
+ }) => {
611
+ const numberValue = Number(value);
612
+ if (numberValue === 0) {
613
+ return formatNumeral({
614
+ locale: numberLocale,
615
+ number: numberValue
616
+ });
617
+ }
618
+ if (numberFormat === "duration") {
619
+ return formatDuration({
620
+ display: "narrow",
621
+ locale: textLocale,
622
+ milliseconds: numberValue
623
+ });
624
+ }
625
+ const maxValue = tickPositions && tickPositions.length > 0 ? tickPositions[tickPositions.length - 1] : void 0;
626
+ const abbreviate = typeof maxValue === "number" && maxValue > 9999 ? 1e3 : true;
627
+ return formatNumeral({
628
+ abbreviate,
629
+ currency,
630
+ format: numberFormat,
631
+ locale: numberLocale,
632
+ number: numberValue
633
+ });
634
+ };
635
+
593
636
  // src/helpers/xAxisLabelFormatter.ts
594
637
  var xAxisLabelFormatter = ({
595
638
  textLocale,
@@ -599,6 +642,9 @@ var xAxisLabelFormatter = ({
599
642
  // optional
600
643
  timeFormat = "12"
601
644
  }) => {
645
+ if (typeof value === "string") {
646
+ return `<span>${value}</span>`;
647
+ }
602
648
  const tickIndex = tickPositions.indexOf(value);
603
649
  const isFirst = tickIndex === 0;
604
650
  const previousValue = tickPositions[tickIndex - 1];
@@ -644,39 +690,227 @@ var xAxisLabelFormatter = ({
644
690
  return `<span>${firstPart}</span>${secondPart ? `<span>${secondPart}</span>` : ""}`;
645
691
  };
646
692
 
647
- // src/helpers/yAxisLabelFormatter.ts
648
- import { formatDuration } from "@sproutsocial/seeds-react-duration";
649
- import { formatNumeral } from "@sproutsocial/seeds-react-numeral";
650
- var yAxisLabelFormatter = ({
651
- numberLocale,
652
- textLocale,
653
- tickPositions,
654
- value,
655
- currency = "USD",
656
- numberFormat = "decimal"
657
- }) => {
658
- const numberValue = Number(value);
659
- if (numberValue === 0) {
660
- return formatNumeral({
661
- locale: numberLocale,
662
- number: numberValue
663
- });
664
- }
665
- if (numberFormat === "duration") {
666
- return formatDuration({
667
- display: "narrow",
668
- locale: textLocale,
669
- milliseconds: numberValue
670
- });
693
+ // src/helpers/isHourlyTimeData.ts
694
+ function isHourlyTimeData(data) {
695
+ if (!data.length) return false;
696
+ const xVals = data.flatMap((series) => series.points.map((p) => p.x));
697
+ if (!xVals.length) return false;
698
+ if (xVals.some((x) => typeof x === "string")) return false;
699
+ const dates = xVals.map((x) => {
700
+ const d = new Date(x);
701
+ return `${d.getUTCFullYear()}-${d.getUTCMonth()}-${d.getUTCDate()}`;
702
+ });
703
+ const uniqueDates = new Set(dates);
704
+ if (uniqueDates.size !== 1) return false;
705
+ return xVals.every((x) => {
706
+ const d = new Date(x);
707
+ const hour = d.getUTCHours();
708
+ const minutes = d.getUTCMinutes();
709
+ const seconds = d.getUTCSeconds();
710
+ return hour >= 0 && hour <= 23 && minutes === 0 && seconds === 0;
711
+ });
712
+ }
713
+
714
+ // src/helpers/isCategoricalHourData.ts
715
+ function isCategoricalHourData(data) {
716
+ if (!data.length) return false;
717
+ const xVals = data.flatMap((series) => series.points.map((p) => p.x));
718
+ if (!xVals.length) return false;
719
+ if (xVals.some((x) => typeof x !== "string")) return false;
720
+ const hour12Pattern = /^\d{1,2} (AM|PM)$/;
721
+ const hour24Pattern = /^([01]?\d|2[0-3]):([0-5]\d)$/;
722
+ const stringValues = xVals;
723
+ const allMatch12Hour = stringValues.every((x) => hour12Pattern.test(x));
724
+ const allMatch24Hour = stringValues.every((x) => hour24Pattern.test(x));
725
+ return allMatch12Hour || allMatch24Hour;
726
+ }
727
+
728
+ // src/helpers/getStorybookRandomColor.ts
729
+ var getStorybookRandomColor = () => {
730
+ const letters = "0123456789ABCDEF";
731
+ let color = "#";
732
+ for (let i = 0; i < 6; i++) {
733
+ color += letters[Math.floor(Math.random() * 16)];
671
734
  }
672
- const maxValue = tickPositions && tickPositions.length > 0 ? tickPositions[tickPositions.length - 1] : void 0;
673
- const abbreviate = typeof maxValue === "number" && maxValue > 9999 ? 1e3 : true;
674
- return formatNumeral({
675
- abbreviate,
676
- currency,
677
- format: numberFormat,
678
- locale: numberLocale,
679
- number: numberValue
735
+ return color;
736
+ };
737
+
738
+ // src/helpers/getStorybookCategoricalData.ts
739
+ var getStorybookCategoricalData = ({
740
+ showNulls,
741
+ showNegativeValues,
742
+ numberOfSeries,
743
+ yAxisMax,
744
+ showCustomColors,
745
+ showDashedLines,
746
+ categoryType
747
+ }) => {
748
+ const categoryOptions = {
749
+ hours: [
750
+ "12 AM",
751
+ "1 AM",
752
+ "2 AM",
753
+ "3 AM",
754
+ "4 AM",
755
+ "5 AM",
756
+ "6 AM",
757
+ "7 AM",
758
+ "8 AM",
759
+ "9 AM",
760
+ "10 AM",
761
+ "11 AM",
762
+ "12 PM",
763
+ "1 PM",
764
+ "2 PM",
765
+ "3 PM",
766
+ "4 PM",
767
+ "5 PM",
768
+ "6 PM",
769
+ "7 PM",
770
+ "8 PM",
771
+ "9 PM",
772
+ "10 PM",
773
+ "11 PM"
774
+ ],
775
+ hours24: [
776
+ "00:00",
777
+ "01:00",
778
+ "02:00",
779
+ "03:00",
780
+ "04:00",
781
+ "05:00",
782
+ "06:00",
783
+ "07:00",
784
+ "08:00",
785
+ "09:00",
786
+ "10:00",
787
+ "11:00",
788
+ "12:00",
789
+ "13:00",
790
+ "14:00",
791
+ "15:00",
792
+ "16:00",
793
+ "17:00",
794
+ "18:00",
795
+ "19:00",
796
+ "20:00",
797
+ "21:00",
798
+ "22:00",
799
+ "23:00"
800
+ ],
801
+ days: [
802
+ "Monday",
803
+ "Tuesday",
804
+ "Wednesday",
805
+ "Thursday",
806
+ "Friday",
807
+ "Saturday",
808
+ "Sunday"
809
+ ],
810
+ months: [
811
+ "January",
812
+ "February",
813
+ "March",
814
+ "April",
815
+ "May",
816
+ "June",
817
+ "July",
818
+ "August",
819
+ "September",
820
+ "October",
821
+ "November",
822
+ "December"
823
+ ],
824
+ custom: [
825
+ "Category A",
826
+ "Category B",
827
+ "Category C",
828
+ "Category D",
829
+ "Category E"
830
+ ]
831
+ };
832
+ const categories = categoryOptions[categoryType];
833
+ const numberOfPoints = categories.length;
834
+ return [...Array(numberOfSeries).keys()].map((seriesIndex) => {
835
+ let nullsArray = [];
836
+ if (showNulls) {
837
+ const nullsArrayLength = Math.floor(numberOfPoints / 5);
838
+ nullsArray = [...Array(nullsArrayLength)].map(
839
+ () => Math.floor(Math.random() * numberOfPoints)
840
+ );
841
+ }
842
+ return {
843
+ points: categories.map((category, pointIndex) => {
844
+ const showNull = nullsArray && nullsArray.length > 0 ? nullsArray.includes(pointIndex) : false;
845
+ const randomValue = Math.random();
846
+ const positiveOrNegativeYAxisMax = showNegativeValues ? randomValue < 0.5 ? -yAxisMax : yAxisMax : yAxisMax;
847
+ return {
848
+ x: category,
849
+ y: showNull ? null : Math.floor(randomValue * positiveOrNegativeYAxisMax)
850
+ };
851
+ }),
852
+ name: `Series ${seriesIndex + 1}`,
853
+ styles: {
854
+ color: showCustomColors ? getStorybookRandomColor() : void 0,
855
+ pattern: showDashedLines ? "dashed" : "solid"
856
+ }
857
+ };
858
+ });
859
+ };
860
+
861
+ // src/helpers/getStorybookSparseTimelineData.ts
862
+ var getStorybookSparseTimelineData = ({
863
+ showNulls,
864
+ showNegativeValues,
865
+ numberOfSeries,
866
+ yAxisMax,
867
+ showCustomColors,
868
+ showDashedLines,
869
+ numberOfPoints = 5,
870
+ timeSpanHours = 24
871
+ }) => {
872
+ const seriesNames = [
873
+ "Posts Published",
874
+ "Comments",
875
+ "Likes",
876
+ "Shares",
877
+ "Clicks",
878
+ "Views",
879
+ "Saves",
880
+ "Reposts",
881
+ "Mentions",
882
+ "Reactions"
883
+ ];
884
+ const baseDate = /* @__PURE__ */ new Date("2024-01-01T00:00:00Z");
885
+ const timeSpanMs = timeSpanHours * 60 * 60 * 1e3;
886
+ return [...Array(numberOfSeries).keys()].map((seriesIndex) => {
887
+ let nullsArray = [];
888
+ if (showNulls) {
889
+ const nullsArrayLength = Math.floor(numberOfPoints / 5);
890
+ nullsArray = [...Array(nullsArrayLength)].map(
891
+ () => Math.floor(Math.random() * numberOfPoints)
892
+ );
893
+ }
894
+ const timestamps = [...Array(numberOfPoints)].map(() => {
895
+ const randomOffset = Math.random() * timeSpanMs;
896
+ return baseDate.getTime() + randomOffset;
897
+ }).sort((a, b) => a - b);
898
+ return {
899
+ points: timestamps.map((timestamp, pointIndex) => {
900
+ const showNull = nullsArray && nullsArray.length > 0 ? nullsArray.includes(pointIndex) : false;
901
+ const randomValue = Math.random();
902
+ const positiveOrNegativeYAxisMax = showNegativeValues ? randomValue < 0.5 ? -yAxisMax : yAxisMax : yAxisMax;
903
+ return {
904
+ x: timestamp,
905
+ y: showNull ? null : Math.floor(randomValue * positiveOrNegativeYAxisMax)
906
+ };
907
+ }),
908
+ name: seriesNames[seriesIndex] || `Series ${seriesIndex + 1}`,
909
+ styles: {
910
+ color: showCustomColors ? getStorybookRandomColor() : void 0,
911
+ pattern: showDashedLines ? "dashed" : "solid"
912
+ }
913
+ };
680
914
  });
681
915
  };
682
916
 
@@ -766,7 +1000,20 @@ var timeSeriesChartOptions = _.merge({}, baseChartOptions, {
766
1000
  softMin: 0,
767
1001
  title: {
768
1002
  text: void 0
769
- }
1003
+ },
1004
+ plotLines: [
1005
+ /* Adds a custom plotLine at y=0 to represent the chart baseline.
1006
+ This approach allows full control over the line's appearance (e.g., color, z-index),
1007
+ unlike relying on the default xAxis line, which always renders at the bottom of the chart
1008
+ even when y=0 appears elsewhere (e.g., with negative values).
1009
+ */
1010
+ {
1011
+ className: "y-axis-zero-line",
1012
+ value: 0,
1013
+ zIndex: 3
1014
+ // ensures the line appears over the default y=0 line, which we can't seleect as specifically
1015
+ }
1016
+ ]
770
1017
  }
771
1018
  });
772
1019
  var lineChartOptions = _.merge({}, timeSeriesChartOptions, {
@@ -780,6 +1027,30 @@ var areaChartOptions = _.merge({}, timeSeriesChartOptions, {
780
1027
  }
781
1028
  }
782
1029
  });
1030
+ var columnChartOptions = _.merge({}, timeSeriesChartOptions, {
1031
+ plotOptions: {
1032
+ column: {
1033
+ stacking: "normal",
1034
+ pointPadding: 0.25,
1035
+ groupPadding: 0.25,
1036
+ borderRadius: 4
1037
+ }
1038
+ },
1039
+ xAxis: {
1040
+ crosshair: false
1041
+ },
1042
+ yAxis: {
1043
+ plotLines: [
1044
+ {
1045
+ // For stacked column charts with visible borders (e.g., white for contrast),
1046
+ // we raise the zIndex of the y=0 plotLine to ensure it remains visible
1047
+ // and isn't visually covered by overlapping column borders.
1048
+ zIndex: 5
1049
+ }
1050
+ ]
1051
+ }
1052
+ });
1053
+ var VERTICAL_BAR_CHART_DEFAULT_SERIES_LIMIT = 10;
783
1054
  var DONUT_CHART_HALO_SIZE = 10;
784
1055
  var DONUT_CHART_HEIGHT = 250 + DONUT_CHART_HALO_SIZE * 2;
785
1056
  var DONUT_CHART_WIDTH = DONUT_CHART_HEIGHT;
@@ -814,7 +1085,8 @@ var useTimeSeriesChartOptions = ({
814
1085
  numberFormat = "decimal",
815
1086
  yAxisLabelFormatter: yAxisLabelFormatter2,
816
1087
  onClick,
817
- timeFormat = "12"
1088
+ timeFormat = "12",
1089
+ xAxisLabelFormatter: xAxisLabelFormatter2
818
1090
  }) => {
819
1091
  const [chart, setChart] = useState3(null);
820
1092
  const callback = useCallback((chartInstance) => {
@@ -848,7 +1120,24 @@ var useTimeSeriesChartOptions = ({
848
1120
  }
849
1121
  };
850
1122
  const chartMarginTop = xAnnotations ? 24 : null;
851
- const baseChartOptions2 = seriesType === "areaspline" ? areaChartOptions : lineChartOptions;
1123
+ const getBaseChartOptions = (type) => {
1124
+ switch (type) {
1125
+ case "areaspline":
1126
+ return areaChartOptions;
1127
+ case "spline":
1128
+ return lineChartOptions;
1129
+ case "column":
1130
+ return columnChartOptions;
1131
+ default:
1132
+ return lineChartOptions;
1133
+ }
1134
+ };
1135
+ const baseChartOptions2 = getBaseChartOptions(seriesType);
1136
+ const hasCategoricalData = data.some(
1137
+ (series) => series.points.some((point) => typeof point.x === "string")
1138
+ );
1139
+ const categories = hasCategoricalData ? data[0]?.points.map((point) => point.x) || [] : [];
1140
+ const hasSparseTimelineData = !hasCategoricalData && seriesType === "column";
852
1141
  const options = useMemo(() => {
853
1142
  return merge({}, baseChartOptions2, {
854
1143
  accessibility: {
@@ -889,6 +1178,61 @@ var useTimeSeriesChartOptions = ({
889
1178
  return onClick({ x: event.point.x });
890
1179
  } : void 0
891
1180
  }
1181
+ },
1182
+ column: {
1183
+ // For sparse timeline data, set explicit point width to make columns wider
1184
+ ...hasSparseTimelineData && {
1185
+ pointWidth: 20
1186
+ // Fixed width in pixels for sparse timeline data
1187
+ },
1188
+ point: {
1189
+ events: {
1190
+ /*
1191
+ Custom hover behavior for multi-series column charts.
1192
+
1193
+ In multi-series column charts, multiple columns
1194
+ can share the same x-axis position (stacked or grouped). When hovering
1195
+ over one column, we want ALL columns at that x-position to highlight
1196
+ together for better visual feedback. Simple CSS :hover only affects the
1197
+ individual element being hovered, not related columns at the same position.
1198
+
1199
+ This custom behavior ensures that when you hover over any column at a
1200
+ specific x-position, all columns at that same position get the "column-hover"
1201
+ class, creating a unified highlight effect across all series.
1202
+ */
1203
+ mouseOver: function() {
1204
+ const x = this.x;
1205
+ if (this.series && this.series.chart && this.series.chart.series) {
1206
+ this.series.chart.series.forEach((series) => {
1207
+ if (series.type === "column") {
1208
+ series.data.forEach((point) => {
1209
+ if (point.x === x) {
1210
+ const el = point.graphic && point.graphic.element;
1211
+ if (el) {
1212
+ el.classList.add("column-hover");
1213
+ }
1214
+ }
1215
+ });
1216
+ }
1217
+ });
1218
+ }
1219
+ },
1220
+ mouseOut: function() {
1221
+ if (this.series && this.series.chart && this.series.chart.series) {
1222
+ this.series.chart.series.forEach((series) => {
1223
+ if (series.type === "column") {
1224
+ series.data.forEach((point) => {
1225
+ const el = point.graphic && point.graphic.element;
1226
+ if (el) {
1227
+ el.classList.remove("column-hover");
1228
+ }
1229
+ });
1230
+ }
1231
+ });
1232
+ }
1233
+ }
1234
+ }
1235
+ }
892
1236
  }
893
1237
  },
894
1238
  series: transformDataToSeries({ data }, seriesType),
@@ -898,14 +1242,37 @@ var useTimeSeriesChartOptions = ({
898
1242
  xAxis: {
899
1243
  labels: {
900
1244
  formatter: function() {
901
- return xAxisLabelFormatter({
902
- textLocale,
903
- tickPositions: this.axis.tickPositions || [],
904
- timeFormat,
905
- unitName: this.tickPositionInfo.unitName,
906
- value: this.value
907
- });
1245
+ const tickPositions = (this.axis.tickPositions || []).map(
1246
+ Number
1247
+ );
1248
+ const value = this.value;
1249
+ const unitName = this.tickPositionInfo?.unitName;
1250
+ if (typeof xAxisLabelFormatter2 === "function") {
1251
+ return xAxisLabelFormatter2({
1252
+ textLocale,
1253
+ tickPositions,
1254
+ timeFormat,
1255
+ unitName,
1256
+ value
1257
+ });
1258
+ }
1259
+ if (typeof xAxisLabelFormatterDefault === "function") {
1260
+ return xAxisLabelFormatterDefault({
1261
+ textLocale,
1262
+ tickPositions,
1263
+ timeFormat,
1264
+ unitName,
1265
+ value
1266
+ });
1267
+ }
1268
+ return "";
908
1269
  }
1270
+ },
1271
+ // Override xAxis type for categorical data
1272
+ ...hasCategoricalData && {
1273
+ type: "category",
1274
+ categories,
1275
+ crosshair: false
909
1276
  }
910
1277
  },
911
1278
  yAxis: {
@@ -928,8 +1295,11 @@ var useTimeSeriesChartOptions = ({
928
1295
  });
929
1296
  }, [
930
1297
  baseChartOptions2,
1298
+ categories,
931
1299
  currency,
932
1300
  data,
1301
+ hasCategoricalData,
1302
+ hasSparseTimelineData,
933
1303
  numberFormat,
934
1304
  numberLocale,
935
1305
  onClick,
@@ -937,10 +1307,12 @@ var useTimeSeriesChartOptions = ({
937
1307
  textLocale,
938
1308
  timeFormat,
939
1309
  xAnnotations,
940
- yAxisLabelFormatter2
1310
+ yAxisLabelFormatter2,
1311
+ xAxisLabelFormatter2
941
1312
  ]);
942
1313
  return { options, chart, callback };
943
1314
  };
1315
+ var xAxisLabelFormatterDefault = xAxisLabelFormatter;
944
1316
 
945
1317
  // src/styles/chartStyles.ts
946
1318
  import { css as css2, createGlobalStyle } from "styled-components";
@@ -959,6 +1331,8 @@ var GlobalChartStyleOverrides = createGlobalStyle`
959
1331
  }
960
1332
  `;
961
1333
  var baseChartStyles = css2`
1334
+ --highcharts-background-color: ${({ theme: theme14 }) => theme14.colors.container.background.base};
1335
+
962
1336
  // set color variables and map to each series
963
1337
  ${({ $colors }) => $colors.map((color, index) => {
964
1338
  const chartColor = color || theme6.colors.DATAVIZ_COLORS_LIST[index];
@@ -1002,9 +1376,11 @@ var timeSeriesChartStyles = css2`
1002
1376
  .highcharts-grid-line {
1003
1377
  stroke: ${({ theme: theme14 }) => theme14.colors.container.border.base};
1004
1378
  }
1379
+
1005
1380
  .highcharts-axis-line {
1006
1381
  stroke: ${({ theme: theme14 }) => theme14.colors.container.border.base};
1007
1382
  }
1383
+
1008
1384
  .highcharts-tick {
1009
1385
  stroke: none;
1010
1386
  }
@@ -1045,6 +1421,10 @@ var timeSeriesChartStyles = css2`
1045
1421
  fill: transparent;
1046
1422
  cursor: pointer;
1047
1423
  }`}
1424
+
1425
+ path.highcharts-plot-line.y-axis-zero-line {
1426
+ stroke: ${({ theme: theme14 }) => theme14.colors.container.border.decorative.neutral};
1427
+ }
1048
1428
  `;
1049
1429
  var lineChartStyles = css2`
1050
1430
  ${timeSeriesChartStyles}
@@ -1729,18 +2109,25 @@ export {
1729
2109
  LineChart,
1730
2110
  NetworkColorBox,
1731
2111
  TIME_SERIES_CHART_HEIGHT,
2112
+ VERTICAL_BAR_CHART_DEFAULT_SERIES_LIMIT,
1732
2113
  areaChartOptions,
1733
2114
  areaChartStyles,
1734
2115
  baseChartOptions,
1735
2116
  baseChartStyles,
2117
+ columnChartOptions,
1736
2118
  donutChartOptions,
1737
2119
  donutChartStyles,
1738
2120
  generateChartTooltipPortalId,
1739
2121
  getDatavizColor,
1740
2122
  getDatavizColorWithAlpha,
1741
2123
  getDatavizOpacity,
2124
+ getStorybookCategoricalData,
2125
+ getStorybookSparseTimelineData,
2126
+ isCategoricalHourData,
2127
+ isHourlyTimeData,
1742
2128
  lineChartOptions,
1743
2129
  lineChartStyles,
2130
+ timeSeriesChartOptions,
1744
2131
  timeSeriesChartStyles,
1745
2132
  transformDataToSeries,
1746
2133
  transformTimeSeriesTooltipData,