@redsift/dashboard 8.0.0 → 8.0.1

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 (155) hide show
  1. package/.env +2 -0
  2. package/coverage/clover.xml +509 -0
  3. package/coverage/coverage-final.json +22 -0
  4. package/coverage/lcov-report/ConnectedDataGrid/ConnectedDataGrid.tsx.html +193 -0
  5. package/coverage/lcov-report/ConnectedDataGrid/index.html +131 -0
  6. package/coverage/lcov-report/ConnectedDataGrid/index.ts.html +85 -0
  7. package/coverage/lcov-report/Dashboard/Dashboard.tsx.html +229 -0
  8. package/coverage/lcov-report/Dashboard/context.ts.html +118 -0
  9. package/coverage/lcov-report/Dashboard/index.html +176 -0
  10. package/coverage/lcov-report/Dashboard/index.ts.html +97 -0
  11. package/coverage/lcov-report/Dashboard/reducer.ts.html +139 -0
  12. package/coverage/lcov-report/Dashboard/types.ts.html +190 -0
  13. package/coverage/lcov-report/HorizontalBarChart/HorizontalBarChart.tsx.html +430 -0
  14. package/coverage/lcov-report/HorizontalBarChart/index.html +161 -0
  15. package/coverage/lcov-report/HorizontalBarChart/index.ts.html +88 -0
  16. package/coverage/lcov-report/HorizontalBarChart/styles.ts.html +217 -0
  17. package/coverage/lcov-report/HorizontalBarChart/types.ts.html +184 -0
  18. package/coverage/lcov-report/PieChart/PieChart.tsx.html +736 -0
  19. package/coverage/lcov-report/PieChart/index.html +161 -0
  20. package/coverage/lcov-report/PieChart/index.ts.html +88 -0
  21. package/coverage/lcov-report/PieChart/styles.ts.html +244 -0
  22. package/coverage/lcov-report/PieChart/types.ts.html +184 -0
  23. package/coverage/lcov-report/base.css +224 -0
  24. package/coverage/lcov-report/block-navigation.js +87 -0
  25. package/coverage/lcov-report/components/ChartEmptyState/ChartEmptyState.tsx.html +679 -0
  26. package/coverage/lcov-report/components/ChartEmptyState/index.html +146 -0
  27. package/coverage/lcov-report/components/ChartEmptyState/index.ts.html +91 -0
  28. package/coverage/lcov-report/components/ChartEmptyState/styles.ts.html +184 -0
  29. package/coverage/lcov-report/components/ConnectedDataGrid/ConnectedDataGrid.tsx.html +181 -0
  30. package/coverage/lcov-report/components/ConnectedDataGrid/index.html +131 -0
  31. package/coverage/lcov-report/components/ConnectedDataGrid/index.ts.html +85 -0
  32. package/coverage/lcov-report/components/CrossfilterRegistry/CrossfilterRegistry.ts.html +163 -0
  33. package/coverage/lcov-report/components/CrossfilterRegistry/index.html +131 -0
  34. package/coverage/lcov-report/components/CrossfilterRegistry/index.ts.html +88 -0
  35. package/coverage/lcov-report/components/Dashboard/Dashboard.tsx.html +289 -0
  36. package/coverage/lcov-report/components/Dashboard/context.ts.html +115 -0
  37. package/coverage/lcov-report/components/Dashboard/index.html +176 -0
  38. package/coverage/lcov-report/components/Dashboard/index.ts.html +97 -0
  39. package/coverage/lcov-report/components/Dashboard/reducer.ts.html +382 -0
  40. package/coverage/lcov-report/components/Dashboard/types.ts.html +226 -0
  41. package/coverage/lcov-report/components/DataGrid/DataGrid.tsx.html +202 -0
  42. package/coverage/lcov-report/components/DataGrid/index.html +131 -0
  43. package/coverage/lcov-report/components/DataGrid/index.ts.html +91 -0
  44. package/coverage/lcov-report/components/EmptyChart/EmptyChart.tsx.html +244 -0
  45. package/coverage/lcov-report/components/EmptyChart/index.html +146 -0
  46. package/coverage/lcov-report/components/EmptyChart/index.ts.html +91 -0
  47. package/coverage/lcov-report/components/EmptyChart/styles.ts.html +241 -0
  48. package/coverage/lcov-report/components/HorizontalBarChart/HorizontalBarChart.tsx.html +1063 -0
  49. package/coverage/lcov-report/components/HorizontalBarChart/index.html +161 -0
  50. package/coverage/lcov-report/components/HorizontalBarChart/index.ts.html +91 -0
  51. package/coverage/lcov-report/components/HorizontalBarChart/styles.ts.html +385 -0
  52. package/coverage/lcov-report/components/HorizontalBarChart/types.ts.html +328 -0
  53. package/coverage/lcov-report/components/PDFExportButton/PdfDocument.tsx.html +688 -0
  54. package/coverage/lcov-report/components/PDFExportButton/PdfExportButton.tsx.html +583 -0
  55. package/coverage/lcov-report/components/PDFExportButton/index.html +161 -0
  56. package/coverage/lcov-report/components/PDFExportButton/index.ts.html +88 -0
  57. package/coverage/lcov-report/components/PDFExportButton/styles.ts.html +532 -0
  58. package/coverage/lcov-report/components/PDFExportButton/utils.ts.html +283 -0
  59. package/coverage/lcov-report/components/PieChart/PieChart.tsx.html +1363 -0
  60. package/coverage/lcov-report/components/PieChart/index.html +161 -0
  61. package/coverage/lcov-report/components/PieChart/index.ts.html +91 -0
  62. package/coverage/lcov-report/components/PieChart/styles.ts.html +388 -0
  63. package/coverage/lcov-report/components/PieChart/types.ts.html +325 -0
  64. package/coverage/lcov-report/components/ResetButton/ResetButton.tsx.html +160 -0
  65. package/coverage/lcov-report/components/ResetButton/index.html +131 -0
  66. package/coverage/lcov-report/components/ResetButton/index.ts.html +91 -0
  67. package/coverage/lcov-report/components/ScatterPlot/ScatterPlot.tsx.html +2881 -0
  68. package/coverage/lcov-report/components/ScatterPlot/index.html +176 -0
  69. package/coverage/lcov-report/components/ScatterPlot/index.ts.html +91 -0
  70. package/coverage/lcov-report/components/ScatterPlot/styles.ts.html +505 -0
  71. package/coverage/lcov-report/components/ScatterPlot/types.ts.html +370 -0
  72. package/coverage/lcov-report/components/ScatterPlot/utils.ts.html +136 -0
  73. package/coverage/lcov-report/components/StaticPieChart/StaticPieChart.tsx.html +286 -0
  74. package/coverage/lcov-report/components/StaticPieChart/index.html +131 -0
  75. package/coverage/lcov-report/components/StaticPieChart/index.ts.html +88 -0
  76. package/coverage/lcov-report/components/TimeSeriesBarChart/TimeSeriesBarChart.tsx.html +1744 -0
  77. package/coverage/lcov-report/components/TimeSeriesBarChart/index.html +161 -0
  78. package/coverage/lcov-report/components/TimeSeriesBarChart/index.ts.html +91 -0
  79. package/coverage/lcov-report/components/TimeSeriesBarChart/styles.ts.html +361 -0
  80. package/coverage/lcov-report/components/TimeSeriesBarChart/types.ts.html +319 -0
  81. package/coverage/lcov-report/components/WithFilters/FilterableBarChart.tsx.html +628 -0
  82. package/coverage/lcov-report/components/WithFilters/FilterableDataGrid.tsx.html +220 -0
  83. package/coverage/lcov-report/components/WithFilters/FilterablePieChart.tsx.html +622 -0
  84. package/coverage/lcov-report/components/WithFilters/FilterableScatterPlot.tsx.html +1090 -0
  85. package/coverage/lcov-report/components/WithFilters/WithFilters.tsx.html +172 -0
  86. package/coverage/lcov-report/components/WithFilters/index.html +191 -0
  87. package/coverage/lcov-report/components/WithFilters/index.ts.html +91 -0
  88. package/coverage/lcov-report/components/index.html +116 -0
  89. package/coverage/lcov-report/components/index.ts.html +97 -0
  90. package/coverage/lcov-report/favicon.png +0 -0
  91. package/coverage/lcov-report/hooks/index.html +116 -0
  92. package/coverage/lcov-report/hooks/useCategoricalChartAsListbox.ts.html +478 -0
  93. package/coverage/lcov-report/hooks/useChartAsListbox.ts.html +655 -0
  94. package/coverage/lcov-report/index.html +206 -0
  95. package/coverage/lcov-report/prettify.css +1 -0
  96. package/coverage/lcov-report/prettify.js +2 -0
  97. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  98. package/coverage/lcov-report/sorter.js +196 -0
  99. package/coverage/lcov-report/utils/groupReduceCount.ts.html +94 -0
  100. package/coverage/lcov-report/utils/groupReduceSum.ts.html +97 -0
  101. package/coverage/lcov-report/utils/groupReducers/groupReduceCount.ts.html +100 -0
  102. package/coverage/lcov-report/utils/groupReducers/groupReduceSum.ts.html +103 -0
  103. package/coverage/lcov-report/utils/groupReducers/index.html +146 -0
  104. package/coverage/lcov-report/utils/groupReducers/index.ts.html +91 -0
  105. package/coverage/lcov-report/utils/index.html +116 -0
  106. package/coverage/lcov-report/utils/index.ts.html +88 -0
  107. package/coverage/lcov.info +1070 -0
  108. package/dist/package.json +113 -0
  109. package/index.ts +1 -0
  110. package/jest.config.js +3 -0
  111. package/package.json +2 -3
  112. package/rollup.config.js +13 -0
  113. package/src/components/ChartEmptyState/ChartEmptyState.stories.tsx +23 -0
  114. package/src/components/ChartEmptyState/ChartEmptyState.tsx +198 -0
  115. package/src/components/ChartEmptyState/index.ts +2 -0
  116. package/src/components/ChartEmptyState/styles.ts +33 -0
  117. package/src/components/ChartEmptyState/types.ts +15 -0
  118. package/src/components/CrossfilterRegistry/CrossfilterRegistry.ts +26 -0
  119. package/src/components/CrossfilterRegistry/index.ts +1 -0
  120. package/src/components/Dashboard/Dashboard.stories.tsx +602 -0
  121. package/src/components/Dashboard/Dashboard.test.tsx +19 -0
  122. package/src/components/Dashboard/Dashboard.tsx +68 -0
  123. package/src/components/Dashboard/__snapshots__/Dashboard.stories.storyshot +24646 -0
  124. package/src/components/Dashboard/context.ts +10 -0
  125. package/src/components/Dashboard/index.ts +4 -0
  126. package/src/components/Dashboard/reducer.ts +99 -0
  127. package/src/components/Dashboard/types.ts +47 -0
  128. package/src/components/PdfExportButton/PdfDocument.tsx +203 -0
  129. package/src/components/PdfExportButton/PdfExportButton.tsx +168 -0
  130. package/src/components/PdfExportButton/index.ts +3 -0
  131. package/src/components/PdfExportButton/styles.ts +151 -0
  132. package/src/components/PdfExportButton/types.ts +59 -0
  133. package/src/components/TimeSeriesBarChart/TimeSeriesBarChart.tsx +565 -0
  134. package/src/components/TimeSeriesBarChart/index.ts +4 -0
  135. package/src/components/TimeSeriesBarChart/styles.ts +94 -0
  136. package/src/components/TimeSeriesBarChart/types.ts +82 -0
  137. package/src/components/WithFilters/FilterableBarChart.tsx +181 -0
  138. package/src/components/WithFilters/FilterableDataGrid.tsx +45 -0
  139. package/src/components/WithFilters/FilterablePieChart.tsx +179 -0
  140. package/src/components/WithFilters/FilterableScatterPlot.tsx +335 -0
  141. package/src/components/WithFilters/WithFilters.tsx +29 -0
  142. package/src/components/WithFilters/index.ts +2 -0
  143. package/src/components/WithFilters/types.ts +45 -0
  144. package/src/hooks/useCategoricalChartAsListbox.ts +131 -0
  145. package/src/index.ts +8 -0
  146. package/src/types.ts +39 -0
  147. package/src/utils/groupReducers/groupReduceCount.ts +5 -0
  148. package/src/utils/groupReducers/groupReduceSum.ts +6 -0
  149. package/src/utils/groupReducers/index.ts +2 -0
  150. package/src/utils/index.ts +1 -0
  151. package/tsconfig.json +3 -0
  152. /package/{CONTRIBUTING.md → dist/CONTRIBUTING.md} +0 -0
  153. /package/{index.d.ts → dist/index.d.ts} +0 -0
  154. /package/{index.js → dist/index.js} +0 -0
  155. /package/{index.js.map → dist/index.js.map} +0 -0
@@ -0,0 +1,59 @@
1
+ // istanbul ignore file
2
+
3
+ import { GridStateColDef, GridValidRowModel } from '@mui/x-data-grid-pro';
4
+ import { ButtonProps } from '@redsift/design-system';
5
+ import { RefObject } from 'react';
6
+
7
+ interface LocaleText {
8
+ maxSizeDisclaimer?: string;
9
+ }
10
+ export interface PdfExportButtonProps extends ButtonProps {
11
+ /** Ref to the DOM component to export. By default, will be the ref to the parent Dashboard. */
12
+ componentRef?: RefObject<HTMLElement>;
13
+ /** Name of the exported PDF file. */
14
+ fileName?: string;
15
+ /** Text to display at the beginning of the PDF. */
16
+ introduction?: string;
17
+ /** Labels and texts. */
18
+ localeText?: LocaleText;
19
+ /** Image to put at the top of the PDF (SVG format not supported). */
20
+ logo?: string;
21
+ /** Function to be executed when button is clicked. */
22
+ onClick?: () => void;
23
+ /** Color used for the header and footer of the PDF. */
24
+ primaryColor?: string;
25
+ }
26
+
27
+ export type PdfTableColumn = GridStateColDef<any, any, any>;
28
+
29
+ export type PdfStyles = { [key: string]: any };
30
+
31
+ export interface PdfTableRowProps {
32
+ columns: PdfTableColumn[];
33
+ rowData: GridValidRowModel;
34
+ rowIndex: number;
35
+ styles: PdfStyles;
36
+ totalWidth: number;
37
+ }
38
+
39
+ export interface PdfTableProps {
40
+ dataTable: PdfTableData;
41
+ styles: PdfStyles;
42
+ localeText?: LocaleText;
43
+ }
44
+
45
+ export interface PdfTableData {
46
+ columns: PdfTableColumn[];
47
+ data: GridValidRowModel[];
48
+ totalWidth: number;
49
+ dimensions?: { width: number; height: number };
50
+ }
51
+
52
+ export interface PdfDocumentProps {
53
+ dashboardImage: string;
54
+ dataTable?: PdfTableData;
55
+ introduction?: string;
56
+ localeText?: LocaleText;
57
+ logo?: string;
58
+ primaryColor: string;
59
+ }
@@ -0,0 +1,565 @@
1
+ // istanbul ignore file
2
+
3
+ import React, {
4
+ forwardRef,
5
+ RefObject,
6
+ useContext,
7
+ useEffect,
8
+ useId,
9
+ useRef,
10
+ useState,
11
+ } from 'react';
12
+ import classNames from 'classnames';
13
+ import {
14
+ config as dcconfig,
15
+ filterAll as dcfilterAll,
16
+ filters as dcfilters,
17
+ redrawAll as dcredrawAll,
18
+ barChart as dcbarChart,
19
+ BarChart as dcBarChart,
20
+ UnitFunction as dcUnitFunction,
21
+ } from 'dc';
22
+ import {
23
+ scaleTime as d3scaleTime,
24
+ timeFormat as d3timeFormat,
25
+ isoParse as d3isoParse,
26
+ utcParse as d3utcParse,
27
+ timeHour as d3timeHour,
28
+ timeHours as d3timeHours,
29
+ timeDay as d3timeDay,
30
+ timeDays as d3timeDays,
31
+ timeWeek as d3timeWeek,
32
+ timeWeeks as d3timeWeeks,
33
+ timeMonth as d3timeMonth,
34
+ timeMonths as d3timeMonths,
35
+ timeYear as d3timeYear,
36
+ timeYears as d3timeYears,
37
+ } from 'd3';
38
+
39
+ import { Button, Comp } from '@redsift/design-system';
40
+ import { scheme, useColor } from '@redsift/charts';
41
+
42
+ import { DashboardContext, DashboardReducerActionType } from '../Dashboard';
43
+ import {
44
+ TimeSeriesBarChartProps,
45
+ TimeSeriesBarChartSize,
46
+ TimeSeriesBarChartTheme,
47
+ } from './types';
48
+ import { JSONObject, JSONArray } from '../../types';
49
+ import {
50
+ StyledTimeSeriesBarChart,
51
+ StyledTimeSeriesBarChartCaption,
52
+ StyledTimeSeriesBarChartTitle,
53
+ StyledTimeSeriesBarChartContainer,
54
+ } from './styles';
55
+ import { GridFilterItem } from '@redsift/table';
56
+ import { ChartEmptyState } from '../ChartEmptyState';
57
+ import { CrossfilterRegistry } from '../CrossfilterRegistry';
58
+
59
+ const COMPONENT_NAME = 'TimeSeriesBarChart';
60
+ const CLASSNAME = 'redsift-timeseries-barchart';
61
+ const DEFAULT_PROPS: Partial<TimeSeriesBarChartProps> = {
62
+ dateTimeFormat: '%Y-%m-%d %H:%M:%S',
63
+ dateTimeGroup: 'month',
64
+ isResetable: true,
65
+ localeText: {
66
+ resetLabel: 'Reset',
67
+ },
68
+ size: TimeSeriesBarChartSize.medium,
69
+ };
70
+
71
+ type ChartDimensions = {
72
+ width: number;
73
+ height: number;
74
+ marginTop?: number;
75
+ marginRight?: number;
76
+ marginBottom?: number;
77
+ marginLeft?: number;
78
+ };
79
+
80
+ const getStartDate = (dates: Date[]) =>
81
+ new Date(Math.min(...dates.map(Number)));
82
+ const getEndDate = (dates: Date[]) => new Date(Math.max(...dates.map(Number)));
83
+
84
+ const sizeToDimension = (size: TimeSeriesBarChartSize): ChartDimensions => {
85
+ switch (size) {
86
+ case TimeSeriesBarChartSize.small:
87
+ return {
88
+ width: 400,
89
+ height: 200,
90
+ marginLeft: 16,
91
+ marginTop: 16,
92
+ marginRight: 16,
93
+ marginBottom: 32,
94
+ };
95
+ case TimeSeriesBarChartSize.large:
96
+ return {
97
+ width: 1000,
98
+ height: 300,
99
+ marginLeft: 16,
100
+ marginTop: 16,
101
+ marginRight: 16,
102
+ marginBottom: 32,
103
+ };
104
+ case TimeSeriesBarChartSize.medium:
105
+ default:
106
+ return {
107
+ width: 600,
108
+ height: 250,
109
+ marginLeft: 16,
110
+ marginTop: 16,
111
+ marginRight: 16,
112
+ marginBottom: 32,
113
+ };
114
+ }
115
+ };
116
+
117
+ const parseDateTimeGroup = (
118
+ data: JSONArray,
119
+ dateTimeGroup: TimeSeriesBarChartProps['dateTimeGroup'],
120
+ dateTimeFieldName: string
121
+ ) => {
122
+ const dates = [
123
+ ...data.map(
124
+ (d: (typeof data)[number]) => new Date(d[dateTimeFieldName] as string)
125
+ ),
126
+ ];
127
+ const startDate = getStartDate(dates);
128
+ const endDate = getEndDate(dates);
129
+
130
+ switch (dateTimeGroup) {
131
+ case 'hour':
132
+ return {
133
+ group: (g: Date) => d3timeHour(g),
134
+ round: (x: Date) => d3timeHour(x),
135
+ x: d3scaleTime().domain([
136
+ d3timeHour.offset(startDate, -1),
137
+ d3timeHour.offset(endDate, 2),
138
+ ]),
139
+ xUnits: d3timeHours,
140
+ };
141
+ case 'day':
142
+ return {
143
+ group: (g: Date) => d3timeDay(g),
144
+ round: (x: Date) => d3timeDay(x),
145
+ x: d3scaleTime().domain([
146
+ d3timeDay.offset(startDate, -1),
147
+ d3timeDay.offset(endDate, 2),
148
+ ]),
149
+ xUnits: d3timeDays,
150
+ };
151
+ case 'week':
152
+ return {
153
+ group: (g: Date) => d3timeWeek(g),
154
+ round: (x: Date) => d3timeWeek(x),
155
+ x: d3scaleTime().domain([
156
+ d3timeWeek.offset(startDate, -1),
157
+ d3timeWeek.offset(endDate, 2),
158
+ ]),
159
+ xUnits: d3timeWeeks,
160
+ };
161
+ case 'year':
162
+ return {
163
+ group: (g: Date) => d3timeYear(g),
164
+ round: (x: Date) => d3timeYear(x),
165
+ x: d3scaleTime().domain([
166
+ d3timeYear.offset(startDate, -2),
167
+ d3timeYear.offset(endDate, 2),
168
+ ]),
169
+ xUnits: d3timeYears,
170
+ };
171
+ case 'month':
172
+ default:
173
+ return {
174
+ group: (g: Date) => d3timeMonth(g),
175
+ round: (x: Date) => d3timeMonth(x),
176
+ x: d3scaleTime().domain([
177
+ d3timeMonth.offset(startDate, -1),
178
+ d3timeMonth.offset(endDate, 2),
179
+ ]),
180
+ xUnits: d3timeMonths,
181
+ };
182
+ }
183
+ };
184
+
185
+ export const TimeSeriesBarChart: Comp<TimeSeriesBarChartProps, HTMLDivElement> =
186
+ forwardRef((props, ref) => {
187
+ const containerRef = (ref ||
188
+ useRef<HTMLDivElement>()) as RefObject<HTMLDivElement>;
189
+ const chartRef = useRef<HTMLDivElement>() as RefObject<HTMLDivElement>;
190
+
191
+ const id = useId();
192
+ const [chart, setChart] = useState<dcBarChart>();
193
+
194
+ const {
195
+ areXLabelsRotated,
196
+ caption,
197
+ className,
198
+ columnToFilter,
199
+ data: propsData,
200
+ dateTimeFieldName,
201
+ dateTimeFormat = DEFAULT_PROPS.dateTimeFormat,
202
+ dateTimeGroup = DEFAULT_PROPS.dateTimeGroup,
203
+ dimension,
204
+ isResetable,
205
+ localeText,
206
+ onFilter,
207
+ size = DEFAULT_PROPS.size,
208
+ theme = props.stackedCategory
209
+ ? TimeSeriesBarChartTheme.default
210
+ : TimeSeriesBarChartTheme.monochrome,
211
+ title,
212
+ stackedCategory,
213
+ xAxisLabel,
214
+ yAxisLabel,
215
+ ...forwardedProps
216
+ } = props;
217
+
218
+ // Get overriden labels and texts.
219
+ const {
220
+ emptyChartTitle,
221
+ emptyChartSubtitle,
222
+ emptyChartResetLabel,
223
+ resetLabel,
224
+ } = {
225
+ ...DEFAULT_PROPS.localeText,
226
+ ...localeText,
227
+ };
228
+
229
+ // Get charts dimensions based on the selected size.
230
+ const chartDimensions = sizeToDimension(size!);
231
+
232
+ // Get ndx from context or initialize it.
233
+ const { dispatch, state, data } = useContext(DashboardContext);
234
+ const { tableFilters } = state;
235
+
236
+ // Set color scheme.
237
+ const colorScale = useColor({
238
+ data: propsData ?? data,
239
+ theme: theme!,
240
+ category: stackedCategory!,
241
+ });
242
+
243
+ const ndx = CrossfilterRegistry.get(propsData ? propsData : data);
244
+
245
+ // useEffect called only once to initialize the chart.
246
+ useEffect(() => {
247
+ if (chartRef.current) {
248
+ dcconfig.defaultColors(scheme.default);
249
+ const parseTime = d3utcParse(dateTimeFormat!);
250
+ const barChart = dcbarChart(chartRef.current as any);
251
+ const dataset = ndx.all();
252
+
253
+ // Get time methods based on props
254
+ const { group, round, x, xUnits } = parseDateTimeGroup(
255
+ dataset as JSONArray,
256
+ dateTimeGroup,
257
+ dateTimeFieldName
258
+ );
259
+
260
+ // Compute dimension and group.
261
+ const computedDimension = ndx.dimension(
262
+ (d: JSONArray) => parseTime(dimension(d) as string) as Date
263
+ );
264
+ const timeGroup = computedDimension.group(group);
265
+
266
+ // Initialize the chart.
267
+ barChart
268
+ .width(chartDimensions.width)
269
+ .height(chartDimensions.height - (areXLabelsRotated ? 30 : 0))
270
+ .dimension(computedDimension)
271
+ .x(x)
272
+ .xUnits(xUnits as dcUnitFunction)
273
+ .round(round)
274
+ .centerBar(true)
275
+ .alwaysUseRounding(true)
276
+ .renderHorizontalGridLines(true)
277
+ .margins({
278
+ left: chartDimensions.marginLeft! + (yAxisLabel ? 24 : 0),
279
+ top: chartDimensions.marginTop!,
280
+ right: chartDimensions.marginRight!,
281
+ bottom: chartDimensions.marginBottom!,
282
+ })
283
+ .barPadding(0.2);
284
+
285
+ barChart.yAxis().ticks(5);
286
+
287
+ // Add label to axis.
288
+ if (xAxisLabel) {
289
+ barChart.xAxisLabel(xAxisLabel);
290
+ }
291
+ if (yAxisLabel) {
292
+ barChart.yAxisLabel(yAxisLabel);
293
+ }
294
+
295
+ // Rotate labels and reset the height, if asked for.
296
+ if (areXLabelsRotated) {
297
+ barChart.on('pretransition', (rowChart) => {
298
+ barChart
299
+ .select('.axis')
300
+ .attr('text-anchor', 'end')
301
+ .selectAll('text')
302
+ .attr('transform', 'translate(-6,5) rotate(-45)');
303
+ rowChart.select('svg').attr('height', chartDimensions.height);
304
+ });
305
+ }
306
+
307
+ // Set group whether the chart is stacked or not.
308
+ if (!stackedCategory) {
309
+ barChart.group(timeGroup).colors(colorScale as any);
310
+ } else if (dataset.length) {
311
+ if (!Array.isArray(dataset[0][stackedCategory])) {
312
+ const categories = dataset.reduce(
313
+ (acc: string[], curr: JSONObject) => {
314
+ if (acc.indexOf(curr[stackedCategory] as string) === -1) {
315
+ acc.push(curr[stackedCategory] as string);
316
+ }
317
+ return acc;
318
+ },
319
+ []
320
+ );
321
+ const stackedTimeGroup = timeGroup.reduce(
322
+ (p, v) => {
323
+ (p as JSONObject)[v[stackedCategory]] =
324
+ (((p as JSONObject)[v[stackedCategory]] as number) || 0) + 1;
325
+ return p;
326
+ },
327
+ (p, v) => {
328
+ (p as JSONObject)[v[stackedCategory]] =
329
+ (((p as JSONObject)[v[stackedCategory]] as number) || 0) - 1;
330
+ return p;
331
+ },
332
+ () => ({})
333
+ );
334
+ barChart
335
+ .group(
336
+ stackedTimeGroup,
337
+ categories[0], // @ts-expect-error
338
+ (
339
+ (cat) =>
340
+ (d: { key: Date; value: { [key: string]: number } }) =>
341
+ d.value[cat]
342
+ )(categories[0])
343
+ )
344
+ .colors(colorScale as any);
345
+ for (var i = 1; i < categories.length; ++i) {
346
+ barChart.stack(
347
+ stackedTimeGroup,
348
+ categories[i],
349
+ (
350
+ (cat) => (d) =>
351
+ d.value[cat]
352
+ )(categories[i])
353
+ );
354
+ }
355
+ } else {
356
+ const categories = dataset.reduce(
357
+ (acc: string[], curr: JSONObject) => {
358
+ (curr[stackedCategory] as string[]).forEach((val: string) => {
359
+ if (acc.indexOf(val) === -1) {
360
+ acc.push(val);
361
+ }
362
+ });
363
+ return acc;
364
+ },
365
+ []
366
+ );
367
+ const stackedTimeGroup = timeGroup.reduce(
368
+ (p, v) => {
369
+ v[stackedCategory].forEach((val: string) => {
370
+ (p as JSONObject)[val] =
371
+ (((p as JSONObject)[val] as number) || 0) + 1;
372
+ });
373
+ return p;
374
+ },
375
+ (p, v) => {
376
+ v[stackedCategory].forEach((val: string) => {
377
+ (p as JSONObject)[val] =
378
+ (((p as JSONObject)[val] as number) || 0) - 1;
379
+ });
380
+ return p;
381
+ },
382
+ () => ({})
383
+ );
384
+ barChart
385
+ .group(
386
+ stackedTimeGroup,
387
+ categories[0], // @ts-expect-error
388
+ (
389
+ (cat) =>
390
+ (d: { key: Date; value: { [key: string]: number } }) =>
391
+ d.value[cat]
392
+ )(categories[0])
393
+ )
394
+ .colors(colorScale as any);
395
+ for (var i = 1; i < categories.length; ++i) {
396
+ barChart.stack(
397
+ stackedTimeGroup,
398
+ categories[i],
399
+ (
400
+ (cat) =>
401
+ (d: { key: Date; value: { [key: string]: number } }) =>
402
+ d.value[cat]
403
+ )(categories[i])
404
+ );
405
+ }
406
+ }
407
+ }
408
+
409
+ // Dispatch filters to datagrid when a value is selected or unselected.
410
+ const formatTime = d3timeFormat('%Y-%m-%dT%H:%M');
411
+ barChart.on('filtered', () => {
412
+ if (columnToFilter) {
413
+ const dateArray = barChart.filters()?.[0];
414
+ if (Array.isArray(dateArray) && dateArray.length === 2) {
415
+ dispatch?.({
416
+ type: DashboardReducerActionType.FilterTableBatch,
417
+ filter: [
418
+ {
419
+ id: `${barChart.anchorName()}-start`,
420
+ columnField: columnToFilter,
421
+ operatorValue: 'after',
422
+ value: formatTime(dateArray[0]),
423
+ },
424
+ {
425
+ id: `${barChart.anchorName()}-end`,
426
+ columnField: columnToFilter,
427
+ operatorValue: 'onOrBefore',
428
+ value: formatTime(dateArray[1]),
429
+ },
430
+ ],
431
+ });
432
+ }
433
+ }
434
+
435
+ onFilter?.(barChart.filters(), ndx.allFiltered());
436
+ });
437
+
438
+ // Render the chart.
439
+ barChart.render();
440
+
441
+ // Store chart for further use.
442
+ setChart(barChart);
443
+ }
444
+ }, [propsData, data]);
445
+
446
+ // Filter chart if context is updated by an external component.
447
+ const filterStart = tableFilters.find(
448
+ (filter) =>
449
+ filter.columnField === columnToFilter &&
450
+ filter.operatorValue === 'after'
451
+ );
452
+ const filterEnd = tableFilters.find(
453
+ (filter) =>
454
+ filter.columnField === columnToFilter &&
455
+ filter.operatorValue === 'onOrBefore'
456
+ );
457
+ useEffect(() => {
458
+ if (
459
+ chart &&
460
+ filterStart &&
461
+ filterEnd &&
462
+ filterStart.value &&
463
+ filterEnd.value
464
+ ) {
465
+ const chartFiltersObj = chart.filters();
466
+ const chartFilters = chartFiltersObj?.[0];
467
+ const chartFilterStart = chartFilters?.[0];
468
+ const chartFilterEnd = chartFilters?.[1];
469
+ if (
470
+ chartFilterStart !== d3isoParse(filterStart.value) &&
471
+ chartFilterEnd !== d3isoParse(filterEnd.value)
472
+ ) {
473
+ chart.filter(null);
474
+ chart.filter(
475
+ dcfilters.RangedFilter(
476
+ d3isoParse(filterStart.value)?.getTime() || 0,
477
+ d3isoParse(filterEnd.value)?.getTime() || 0
478
+ )
479
+ );
480
+ dcredrawAll();
481
+ }
482
+ }
483
+ }, [filterStart?.value, filterEnd?.value]);
484
+
485
+ return (
486
+ <StyledTimeSeriesBarChart
487
+ {...forwardedProps}
488
+ className={classNames(TimeSeriesBarChart.className, className)}
489
+ ref={containerRef as RefObject<HTMLDivElement>}
490
+ >
491
+ <StyledTimeSeriesBarChartTitle
492
+ className={`${TimeSeriesBarChart.className}__title`}
493
+ alignItems="center"
494
+ >
495
+ {title ? <div id={`id${id}__title`}>{title}</div> : null}
496
+ {chart && isResetable ? (
497
+ <Button
498
+ variant="unstyled"
499
+ onClick={() => {
500
+ chart.filter(null);
501
+ dcredrawAll();
502
+
503
+ if (columnToFilter) {
504
+ dispatch?.({
505
+ type: DashboardReducerActionType.ResetFilter,
506
+ filter: {
507
+ id: `${chart.anchorName()}-start`,
508
+ columnField: columnToFilter,
509
+ operatorValue: 'after',
510
+ } as GridFilterItem,
511
+ });
512
+ dispatch?.({
513
+ type: DashboardReducerActionType.ResetFilter,
514
+ filter: {
515
+ id: `${chart.anchorName()}-end`,
516
+ columnField: columnToFilter,
517
+ operatorValue: 'onOrBefore',
518
+ } as GridFilterItem,
519
+ });
520
+ }
521
+ }}
522
+ >
523
+ {resetLabel}
524
+ </Button>
525
+ ) : null}
526
+ </StyledTimeSeriesBarChartTitle>
527
+
528
+ <StyledTimeSeriesBarChartContainer
529
+ className={`${TimeSeriesBarChart.className}__container`}
530
+ $isEmpty={ndx.all().length === 0}
531
+ >
532
+ <div
533
+ className={`${TimeSeriesBarChart.className}-container__chart`}
534
+ ref={chartRef as RefObject<HTMLDivElement>}
535
+ />
536
+ {ndx.all().length === 0 ? (
537
+ <ChartEmptyState
538
+ title={emptyChartTitle!}
539
+ subtitle={emptyChartSubtitle}
540
+ resetLabel={emptyChartResetLabel}
541
+ onReset={() => {
542
+ dcfilterAll();
543
+ dcredrawAll();
544
+ dispatch?.({
545
+ type: DashboardReducerActionType.ResetFilters,
546
+ });
547
+ }}
548
+ />
549
+ ) : null}
550
+ </StyledTimeSeriesBarChartContainer>
551
+
552
+ {caption ? (
553
+ <StyledTimeSeriesBarChartCaption
554
+ className={`${TimeSeriesBarChart.className}__caption`}
555
+ id={`id${id}__caption`}
556
+ >
557
+ {caption}
558
+ </StyledTimeSeriesBarChartCaption>
559
+ ) : null}
560
+ </StyledTimeSeriesBarChart>
561
+ );
562
+ });
563
+ TimeSeriesBarChart.className = CLASSNAME;
564
+ TimeSeriesBarChart.defaultProps = DEFAULT_PROPS;
565
+ TimeSeriesBarChart.displayName = COMPONENT_NAME;
@@ -0,0 +1,4 @@
1
+ // istanbul ignore file
2
+
3
+ export * from './TimeSeriesBarChart';
4
+ export * from './types';