@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,94 @@
1
+ // istanbul ignore file
2
+
3
+ import styled, { css } from 'styled-components';
4
+ import { Flexbox } from '@redsift/design-system';
5
+ import { StyledTimeSeriesBarChartProps } from './types';
6
+
7
+ /**
8
+ * Component style.
9
+ */
10
+ export const StyledTimeSeriesBarChart = styled.div<StyledTimeSeriesBarChartProps>`
11
+ margin: 16px;
12
+ padding: 16px;
13
+ color: var(--redsift-color-neutral-black);
14
+
15
+ .redsift-timeseries-barchart__chart {
16
+ display: flex;
17
+ font-family: var(--redsift-typography-font-family-source-code-pro);
18
+ font-size: 12px;
19
+ justify-content: center;
20
+ margin: 8px 0;
21
+ }
22
+
23
+ svg > g {
24
+ margin: 8px;
25
+ }
26
+
27
+ .grid-line,
28
+ .axis .grid-line,
29
+ .grid-line line,
30
+ .axis .grid-line line {
31
+ fill: none;
32
+ stroke: #ccc;
33
+ shape-rendering: crispEdges;
34
+ }
35
+
36
+ .axis text {
37
+ font-family: var(--redsift-typography-font-family-source-code-pro);
38
+ font-size: 10px;
39
+ }
40
+
41
+ g.stack rect.bar.deselected {
42
+ fill-opacity: 0.5;
43
+ fill: var(--redsift-color-neutral-midgrey);
44
+ }
45
+
46
+ .brush .custom-brush-handle {
47
+ fill: #eee;
48
+ stroke: #666;
49
+ cursor: ew-resize;
50
+ }
51
+
52
+ text {
53
+ user-select: none;
54
+ }
55
+ `;
56
+
57
+ export const StyledTimeSeriesBarChartTitle = styled(
58
+ Flexbox
59
+ )<StyledTimeSeriesBarChartProps>`
60
+ font-family: var(--redsift-typography-h4-font-family);
61
+ font-size: var(--redsift-typography-h4-font-size);
62
+ font-weight: var(--redsift-typography-h4-font-weight);
63
+ line-height: var(--redsift-typography-h4-line-height);
64
+ `;
65
+
66
+ export const StyledTimeSeriesBarChartCaption = styled.p<StyledTimeSeriesBarChartProps>`
67
+ font-family: var(--redsift-typography-caption-font-family);
68
+ font-size: var(--redsift-typography-caption-font-size);
69
+ font-weight: var(--redsift-typography-caption-font-weight);
70
+ line-height: var(--redsift-typography-caption-line-height);
71
+ `;
72
+
73
+ export const StyledTimeSeriesBarChartContainer = styled.div<{
74
+ $isEmpty: boolean;
75
+ }>`
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 16px;
79
+ font-family: var(--redsift-typography-font-family-source-code-pro);
80
+ font-size: 11px;
81
+ justify-content: center;
82
+ margin: 8px 0;
83
+ position: relative;
84
+
85
+ .redsift-timeseries-barchart-container__chart {
86
+ position: relative;
87
+ ${({ $isEmpty }) =>
88
+ $isEmpty
89
+ ? css`
90
+ display: none;
91
+ `
92
+ : ''};
93
+ }
94
+ `;
@@ -0,0 +1,82 @@
1
+ // istanbul ignore file
2
+
3
+ import { ComponentProps } from 'react';
4
+ import { ValueOf } from '@redsift/design-system';
5
+ import { ColorTheme, DimensionSelector, JSONArray } from '../../types';
6
+
7
+ type SuccessDangerColorTheme = {
8
+ success: string;
9
+ warning: string;
10
+ danger: string;
11
+ neutral?: string;
12
+ };
13
+
14
+ /**
15
+ * Component size.
16
+ */
17
+ export const TimeSeriesBarChartSize = {
18
+ large: 'large',
19
+ medium: 'medium',
20
+ small: 'small',
21
+ } as const;
22
+ export type TimeSeriesBarChartSize = ValueOf<typeof TimeSeriesBarChartSize>;
23
+
24
+ /**
25
+ * Component theme.
26
+ */
27
+ export const TimeSeriesBarChartTheme = ColorTheme;
28
+ export type TimeSeriesBarChartTheme = ValueOf<typeof TimeSeriesBarChartTheme>;
29
+
30
+ export type datetimeEnum = 'hour' | 'day' | 'week' | 'month' | 'year';
31
+
32
+ interface LocaleText {
33
+ emptyChartTitle?: string;
34
+ emptyChartSubtitle?: string;
35
+ emptyChartResetLabel?: string;
36
+ resetLabel?: string;
37
+ }
38
+
39
+ /**
40
+ * Component props.
41
+ */
42
+ export interface TimeSeriesBarChartProps extends ComponentProps<'div'> {
43
+ /** Caption. */
44
+ caption?: string;
45
+ /** Whether the x axis labels are rotated or not. */
46
+ areXLabelsRotated?: boolean;
47
+ /** Field value of the DataGrid column to filter. */
48
+ columnToFilter?: string;
49
+ /** Dataset to use to generate the chart, if no context is provided. */
50
+ data?: JSONArray;
51
+ /** Name of the dataset field that contains the datetime value. */
52
+ dateTimeFieldName: string;
53
+ /** Format used for the date and time values in the dataset by d3.js. Should follow https://github.com/d3/d3-time-format#locale_format. */
54
+ dateTimeFormat?: string;
55
+ /** Datetime unit to used to group values and for x units. */
56
+ dateTimeGroup?: datetimeEnum;
57
+ /** Method that will be used by crossfilter to compute the dimensions of the charts. */
58
+ dimension: DimensionSelector;
59
+ /** Whether the reset button is hidden or not. */
60
+ isResetable?: boolean;
61
+ /** Overriden values for internal labels and texts. */
62
+ localeText?: LocaleText;
63
+ /** Method to call when a filter occured on the chart. */
64
+ onFilter?: (filters?: string[], allFiltered?: JSONArray) => void;
65
+ /** TimeSeriesChart size. */
66
+ size?: TimeSeriesBarChartSize;
67
+ /** Color palette to use. You can choose among the list of available color palette or also choose to use the success/warning/danger theme for which you have to specify which key corresponds to which status. */
68
+ theme?: TimeSeriesBarChartTheme | SuccessDangerColorTheme;
69
+ /** Title. */
70
+ title?: string;
71
+ /** Dataset key to use to stack values. */
72
+ stackedCategory?: string;
73
+ /** X Axis Label. */
74
+ xAxisLabel?: string;
75
+ /** Y Axis Label. */
76
+ yAxisLabel?: string;
77
+ }
78
+
79
+ export type StyledTimeSeriesBarChartProps = Omit<
80
+ TimeSeriesBarChartProps,
81
+ 'dimension' | 'dateTimeFieldName'
82
+ > & {};
@@ -0,0 +1,181 @@
1
+ import { GridFilterItem } from '@redsift/table';
2
+ import { BarDatum } from '@redsift/charts';
3
+ import { Dimension, Group } from '../../types';
4
+ import React, {
5
+ useEffect,
6
+ useContext,
7
+ useId,
8
+ useState,
9
+ isValidElement,
10
+ useRef,
11
+ MutableRefObject,
12
+ } from 'react';
13
+ import { CrossfilterRegistry } from '../CrossfilterRegistry';
14
+ import { DashboardContext, DashboardReducerActionType } from '../Dashboard';
15
+ import { ChartEmptyState } from '../ChartEmptyState';
16
+ import { ChartsWithFiltersProps } from './types';
17
+ import { useCategoricalChartAsListbox } from '@redsift/dashboard/hooks/useCategoricalChartAsListbox';
18
+
19
+ export const FilterableBarChart: React.FC<ChartsWithFiltersProps> = (props) => {
20
+ const {
21
+ children,
22
+ datagridCategoryDimFilter,
23
+ dimension,
24
+ group,
25
+ id: propsId,
26
+ isDimensionArray,
27
+ isResetable,
28
+ localeText,
29
+ onFilter,
30
+ } = props;
31
+ const id = (propsId ?? useId()).replace(/:/g, '');
32
+ const ref = useRef<HTMLDivElement>();
33
+
34
+ const { emptyChartTitle, emptyChartSubtitle, emptyChartResetLabel } = {
35
+ ...localeText,
36
+ };
37
+
38
+ const {
39
+ data,
40
+ dispatch,
41
+ toggleUpdateContext,
42
+ state: { tableFilters },
43
+ } = useContext(DashboardContext);
44
+
45
+ const [ndxDimension, setNdxDimension] = useState<Dimension>();
46
+ const [ndxGroup, setNdxGroup] = useState<Group>();
47
+
48
+ useEffect(() => {
49
+ const computedNdxDimension = CrossfilterRegistry.get(data).dimension(
50
+ dimension,
51
+ isDimensionArray
52
+ );
53
+ const computedNdxGroup = group
54
+ ? group(computedNdxDimension)
55
+ : computedNdxDimension.group();
56
+ setNdxDimension(computedNdxDimension);
57
+ setNdxGroup(computedNdxGroup);
58
+ return function cleanup() {
59
+ computedNdxDimension.filterAll();
60
+ };
61
+ }, [dimension, data, group]);
62
+
63
+ const [filters, setFilters] = useState<string[]>([]);
64
+
65
+ const handleFilter = (category: string) => {
66
+ let newFilters: string[];
67
+ if (filters.includes(category)) {
68
+ newFilters = filters.filter((f) => f !== category);
69
+ } else {
70
+ newFilters = [...filters, category];
71
+ }
72
+
73
+ setFilters(newFilters);
74
+ if (newFilters.length) {
75
+ ndxDimension!.filter((d) => newFilters.includes(d as string));
76
+ } else {
77
+ ndxDimension!.filterAll();
78
+ }
79
+ toggleUpdateContext?.();
80
+
81
+ if (datagridCategoryDimFilter) {
82
+ dispatch?.({
83
+ type: DashboardReducerActionType.FilterTable,
84
+ filter: {
85
+ id: id,
86
+ columnField: datagridCategoryDimFilter.field,
87
+ operatorValue: datagridCategoryDimFilter.operator,
88
+ value: newFilters,
89
+ },
90
+ });
91
+ }
92
+
93
+ if (onFilter) {
94
+ onFilter(newFilters);
95
+ }
96
+ };
97
+
98
+ const resetFilters = () => {
99
+ setFilters([]);
100
+ ndxDimension!.filterAll();
101
+ toggleUpdateContext?.();
102
+
103
+ if (datagridCategoryDimFilter) {
104
+ dispatch?.({
105
+ type: DashboardReducerActionType.ResetFilter,
106
+ filter: {
107
+ id: id,
108
+ columnField: datagridCategoryDimFilter.field,
109
+ operatorValue: datagridCategoryDimFilter.operator,
110
+ } as GridFilterItem,
111
+ });
112
+ }
113
+
114
+ if (onFilter) {
115
+ onFilter();
116
+ }
117
+ };
118
+
119
+ useEffect(() => {
120
+ if (!datagridCategoryDimFilter) {
121
+ return;
122
+ }
123
+
124
+ const updatedFilters = tableFilters.find(
125
+ (filter) =>
126
+ filter.columnField === datagridCategoryDimFilter.field &&
127
+ filter.operatorValue === datagridCategoryDimFilter.operator
128
+ )?.value;
129
+ if (updatedFilters && updatedFilters !== filters) {
130
+ setFilters(updatedFilters);
131
+ if (updatedFilters.length) {
132
+ ndxDimension!.filter((d) => updatedFilters.includes(d));
133
+ } else {
134
+ ndxDimension!.filterAll();
135
+ }
136
+ toggleUpdateContext?.();
137
+ }
138
+ }, [tableFilters]);
139
+
140
+ const emptyComponent = (
141
+ <ChartEmptyState
142
+ title={emptyChartTitle!}
143
+ subtitle={emptyChartSubtitle}
144
+ resetLabel={emptyChartResetLabel}
145
+ onReset={resetFilters}
146
+ />
147
+ );
148
+
149
+ const chartProps = useCategoricalChartAsListbox({
150
+ id,
151
+ ref: ref as MutableRefObject<HTMLDivElement>,
152
+ type: 'bar',
153
+ ndxGroup,
154
+ orientation: 'vertical',
155
+ });
156
+
157
+ const filterProps = {
158
+ barProps: {
159
+ tabIndex: -1,
160
+ },
161
+ barRole: 'option',
162
+ chartProps: chartProps,
163
+ chartRef: ref,
164
+ data: ndxGroup ? JSON.parse(JSON.stringify(ndxGroup.all())) : undefined,
165
+ emptyComponent,
166
+ id,
167
+ isBarSelected: (datum: BarDatum) => {
168
+ return filters.length === 0 || filters.includes(datum.data.key);
169
+ },
170
+ onBarClick: (datum: BarDatum) => {
171
+ handleFilter(datum.data.key);
172
+ },
173
+ onReset: isResetable ? resetFilters : undefined,
174
+ };
175
+
176
+ if (isValidElement(children)) {
177
+ return React.cloneElement(children, { ...filterProps });
178
+ }
179
+
180
+ return null;
181
+ };
@@ -0,0 +1,45 @@
1
+ import { GridFilterModel } from '@redsift/table';
2
+ import React, { useContext, isValidElement, useState, useEffect } from 'react';
3
+ import { DashboardContext, DashboardReducerActionType } from '../Dashboard';
4
+ import { WithFiltersProps } from './types';
5
+
6
+ export const FilterableDataGrid: React.FC<WithFiltersProps> = (props) => {
7
+ const { children, onFilter } = props;
8
+
9
+ const { state, data, dispatch, dataGridApiRef } =
10
+ useContext(DashboardContext);
11
+ const { tableFilters } = state;
12
+
13
+ const [filterModel, setFilterModel] = useState<GridFilterModel>();
14
+ useEffect(() => {
15
+ setFilterModel({
16
+ ...filterModel,
17
+ items: tableFilters,
18
+ });
19
+ }, [tableFilters]);
20
+
21
+ const updateChartFilters = (filterModel: GridFilterModel) => {
22
+ dispatch?.({
23
+ type: DashboardReducerActionType.FilterTableBatch,
24
+ filter: filterModel.items,
25
+ });
26
+ };
27
+
28
+ if (isValidElement(children)) {
29
+ const filterProps = {
30
+ apiRef: children.props.apiRef ?? dataGridApiRef,
31
+ rows: data || [],
32
+ filterModel,
33
+ onFilterModelChange: (filterModel: GridFilterModel) => {
34
+ if (onFilter) {
35
+ onFilter(filterModel);
36
+ }
37
+ updateChartFilters(filterModel);
38
+ },
39
+ };
40
+
41
+ return React.cloneElement(children, { ...filterProps });
42
+ }
43
+
44
+ return null;
45
+ };
@@ -0,0 +1,179 @@
1
+ import { GridFilterItem } from '@redsift/table';
2
+ import { ArcDatum } from '@redsift/charts';
3
+ import { Dimension, Group } from '../../types';
4
+ import React, {
5
+ useEffect,
6
+ useContext,
7
+ useId,
8
+ useState,
9
+ isValidElement,
10
+ useRef,
11
+ MutableRefObject,
12
+ } from 'react';
13
+ import { CrossfilterRegistry } from '../CrossfilterRegistry';
14
+ import { DashboardContext, DashboardReducerActionType } from '../Dashboard';
15
+ import { ChartEmptyState } from '../ChartEmptyState';
16
+ import { ChartsWithFiltersProps } from './types';
17
+ import { useCategoricalChartAsListbox } from '@redsift/dashboard/hooks/useCategoricalChartAsListbox';
18
+
19
+ export const FilterablePieChart: React.FC<ChartsWithFiltersProps> = (props) => {
20
+ const {
21
+ children,
22
+ datagridCategoryDimFilter,
23
+ dimension,
24
+ group,
25
+ id: propsId,
26
+ isDimensionArray,
27
+ isResetable,
28
+ localeText,
29
+ onFilter,
30
+ } = props;
31
+ const id = (propsId ?? useId()).replace(/:/g, '');
32
+ const ref = useRef<HTMLDivElement>();
33
+
34
+ const { emptyChartTitle, emptyChartSubtitle, emptyChartResetLabel } = {
35
+ ...localeText,
36
+ };
37
+
38
+ const {
39
+ data,
40
+ dispatch,
41
+ toggleUpdateContext,
42
+ state: { tableFilters },
43
+ } = useContext(DashboardContext);
44
+
45
+ const [ndxDimension, setNdxDimension] = useState<Dimension>();
46
+ const [ndxGroup, setNdxGroup] = useState<Group>();
47
+
48
+ useEffect(() => {
49
+ const computedNdxDimension = CrossfilterRegistry.get(data).dimension(
50
+ dimension,
51
+ isDimensionArray
52
+ );
53
+ const computedNdxGroup = group
54
+ ? group(computedNdxDimension)
55
+ : computedNdxDimension.group();
56
+ setNdxDimension(computedNdxDimension);
57
+ setNdxGroup(computedNdxGroup);
58
+ return function cleanup() {
59
+ computedNdxDimension.filterAll();
60
+ };
61
+ }, [dimension, data, group]);
62
+
63
+ const [filters, setFilters] = useState<string[]>([]);
64
+
65
+ const handleFilter = (category: string) => {
66
+ let newFilters: string[];
67
+ if (filters.includes(category)) {
68
+ newFilters = filters.filter((f) => f !== category);
69
+ } else {
70
+ newFilters = [...filters, category];
71
+ }
72
+
73
+ setFilters(newFilters);
74
+ if (newFilters.length) {
75
+ ndxDimension!.filter((d) => newFilters.includes(d as string));
76
+ } else {
77
+ ndxDimension!.filterAll();
78
+ }
79
+ toggleUpdateContext?.();
80
+
81
+ if (datagridCategoryDimFilter) {
82
+ dispatch?.({
83
+ type: DashboardReducerActionType.FilterTable,
84
+ filter: {
85
+ id: id,
86
+ columnField: datagridCategoryDimFilter.field,
87
+ operatorValue: datagridCategoryDimFilter.operator,
88
+ value: newFilters,
89
+ },
90
+ });
91
+ }
92
+
93
+ if (onFilter) {
94
+ onFilter(newFilters);
95
+ }
96
+ };
97
+
98
+ const resetFilters = () => {
99
+ setFilters([]);
100
+ ndxDimension!.filterAll();
101
+ toggleUpdateContext?.();
102
+
103
+ if (datagridCategoryDimFilter) {
104
+ dispatch?.({
105
+ type: DashboardReducerActionType.ResetFilter,
106
+ filter: {
107
+ id: id,
108
+ columnField: datagridCategoryDimFilter.field,
109
+ operatorValue: datagridCategoryDimFilter.operator,
110
+ } as GridFilterItem,
111
+ });
112
+ }
113
+
114
+ if (onFilter) {
115
+ onFilter();
116
+ }
117
+ };
118
+
119
+ const updatedFilters = datagridCategoryDimFilter
120
+ ? tableFilters.find(
121
+ (filter) =>
122
+ filter.columnField === datagridCategoryDimFilter.field &&
123
+ filter.operatorValue === datagridCategoryDimFilter.operator
124
+ )?.value
125
+ : null;
126
+ useEffect(() => {
127
+ if (updatedFilters && updatedFilters !== filters) {
128
+ setFilters(updatedFilters);
129
+ if (updatedFilters.length) {
130
+ ndxDimension!.filter((d) => updatedFilters.includes(d));
131
+ } else {
132
+ ndxDimension!.filterAll();
133
+ }
134
+ toggleUpdateContext?.();
135
+ }
136
+ }, [updatedFilters]);
137
+
138
+ const emptyComponent = (
139
+ <ChartEmptyState
140
+ title={emptyChartTitle!}
141
+ subtitle={emptyChartSubtitle}
142
+ resetLabel={emptyChartResetLabel}
143
+ onReset={resetFilters}
144
+ />
145
+ );
146
+
147
+ const chartProps = useCategoricalChartAsListbox({
148
+ id,
149
+ ref: ref as MutableRefObject<HTMLDivElement>,
150
+ type: 'bar',
151
+ ndxGroup,
152
+ orientation: 'horizontal',
153
+ });
154
+
155
+ const filterProps = {
156
+ sliceProps: {
157
+ tabIndex: -1,
158
+ },
159
+ sliceRole: 'option',
160
+ chartProps: chartProps,
161
+ chartRef: ref,
162
+ data: ndxGroup ? JSON.parse(JSON.stringify(ndxGroup.all())) : undefined,
163
+ emptyComponent,
164
+ id,
165
+ isSliceSelected: (datum: ArcDatum) => {
166
+ return filters.length === 0 || filters.includes(datum.data.key);
167
+ },
168
+ onSliceClick: (datum: ArcDatum) => {
169
+ handleFilter(datum.data.key);
170
+ },
171
+ onReset: isResetable ? resetFilters : undefined,
172
+ };
173
+
174
+ if (isValidElement(children)) {
175
+ return React.cloneElement(children, { ...filterProps });
176
+ }
177
+
178
+ return null;
179
+ };