@redsift/dashboard 8.0.0-alpha.8 → 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 (156) 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/{index.js → dist/index.js} +144 -20
  109. package/dist/index.js.map +1 -0
  110. package/dist/package.json +113 -0
  111. package/index.ts +1 -0
  112. package/jest.config.js +3 -0
  113. package/package.json +6 -12
  114. package/rollup.config.js +13 -0
  115. package/src/components/ChartEmptyState/ChartEmptyState.stories.tsx +23 -0
  116. package/src/components/ChartEmptyState/ChartEmptyState.tsx +198 -0
  117. package/src/components/ChartEmptyState/index.ts +2 -0
  118. package/src/components/ChartEmptyState/styles.ts +33 -0
  119. package/src/components/ChartEmptyState/types.ts +15 -0
  120. package/src/components/CrossfilterRegistry/CrossfilterRegistry.ts +26 -0
  121. package/src/components/CrossfilterRegistry/index.ts +1 -0
  122. package/src/components/Dashboard/Dashboard.stories.tsx +602 -0
  123. package/src/components/Dashboard/Dashboard.test.tsx +19 -0
  124. package/src/components/Dashboard/Dashboard.tsx +68 -0
  125. package/src/components/Dashboard/__snapshots__/Dashboard.stories.storyshot +24646 -0
  126. package/src/components/Dashboard/context.ts +10 -0
  127. package/src/components/Dashboard/index.ts +4 -0
  128. package/src/components/Dashboard/reducer.ts +99 -0
  129. package/src/components/Dashboard/types.ts +47 -0
  130. package/src/components/PdfExportButton/PdfDocument.tsx +203 -0
  131. package/src/components/PdfExportButton/PdfExportButton.tsx +168 -0
  132. package/src/components/PdfExportButton/index.ts +3 -0
  133. package/src/components/PdfExportButton/styles.ts +151 -0
  134. package/src/components/PdfExportButton/types.ts +59 -0
  135. package/src/components/TimeSeriesBarChart/TimeSeriesBarChart.tsx +565 -0
  136. package/src/components/TimeSeriesBarChart/index.ts +4 -0
  137. package/src/components/TimeSeriesBarChart/styles.ts +94 -0
  138. package/src/components/TimeSeriesBarChart/types.ts +82 -0
  139. package/src/components/WithFilters/FilterableBarChart.tsx +181 -0
  140. package/src/components/WithFilters/FilterableDataGrid.tsx +45 -0
  141. package/src/components/WithFilters/FilterablePieChart.tsx +179 -0
  142. package/src/components/WithFilters/FilterableScatterPlot.tsx +335 -0
  143. package/src/components/WithFilters/WithFilters.tsx +29 -0
  144. package/src/components/WithFilters/index.ts +2 -0
  145. package/src/components/WithFilters/types.ts +45 -0
  146. package/src/hooks/useCategoricalChartAsListbox.ts +131 -0
  147. package/src/index.ts +8 -0
  148. package/src/types.ts +39 -0
  149. package/src/utils/groupReducers/groupReduceCount.ts +5 -0
  150. package/src/utils/groupReducers/groupReduceSum.ts +6 -0
  151. package/src/utils/groupReducers/index.ts +2 -0
  152. package/src/utils/index.ts +1 -0
  153. package/tsconfig.json +3 -0
  154. package/index.js.map +0 -1
  155. /package/{CONTRIBUTING.md → dist/CONTRIBUTING.md} +0 -0
  156. /package/{index.d.ts → dist/index.d.ts} +0 -0
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { DashboardContextProps, DashboardReducerState } from './types';
3
+
4
+ export const initialState: DashboardReducerState = {
5
+ tableFilters: [],
6
+ };
7
+
8
+ export const DashboardContext = React.createContext<DashboardContextProps>({
9
+ state: initialState,
10
+ } as DashboardContextProps);
@@ -0,0 +1,4 @@
1
+ export * from './Dashboard';
2
+ export * from './context';
3
+ export * from './reducer';
4
+ export * from './types';
@@ -0,0 +1,99 @@
1
+ import { GridFilterItem } from '@redsift/table';
2
+ import {
3
+ DashboardReducerState,
4
+ DashboardReducerAction,
5
+ DashboardReducerActionType,
6
+ } from './types';
7
+
8
+ const areEquals = (
9
+ tableFilter: GridFilterItem,
10
+ actionFilter: GridFilterItem
11
+ ) => {
12
+ return (
13
+ tableFilter.columnField === actionFilter.columnField &&
14
+ tableFilter.operatorValue === actionFilter.operatorValue
15
+ );
16
+ };
17
+
18
+ const addOrUpdateFilter = (
19
+ oldFilters: GridFilterItem[],
20
+ newFilter: GridFilterItem
21
+ ) => {
22
+ if (oldFilters.find((f) => areEquals(f, newFilter))) {
23
+ return oldFilters.map((f) => {
24
+ if (areEquals(f, newFilter)) {
25
+ return newFilter;
26
+ }
27
+ return f;
28
+ });
29
+ } else {
30
+ return [...oldFilters, newFilter];
31
+ }
32
+ };
33
+
34
+ const addOrUpdateFilters = (
35
+ oldFilters: GridFilterItem[],
36
+ newFilters: GridFilterItem[]
37
+ ) => {
38
+ let updatedFilters = oldFilters;
39
+ for (let i = 0; i < newFilters.length; i++) {
40
+ updatedFilters = addOrUpdateFilter(updatedFilters, newFilters[i]);
41
+ }
42
+ return updatedFilters;
43
+ };
44
+
45
+ const replaceFilters = (newFilters: GridFilterItem | GridFilterItem[]) => {
46
+ return Array.isArray(newFilters) ? newFilters : [newFilters];
47
+ };
48
+
49
+ const removeFilter = (
50
+ oldFilters: GridFilterItem[],
51
+ newFilter: GridFilterItem
52
+ ) => {
53
+ return oldFilters.filter((f) => !areEquals(f, newFilter));
54
+ };
55
+
56
+ const removeAllFilters = () => {
57
+ return [] as GridFilterItem[];
58
+ };
59
+
60
+ export const DashboardReducer = (
61
+ state: DashboardReducerState,
62
+ action: DashboardReducerAction
63
+ ): DashboardReducerState => {
64
+ switch (action.type) {
65
+ case DashboardReducerActionType.ResetFilter:
66
+ return {
67
+ ...state,
68
+ tableFilters: removeFilter(
69
+ state.tableFilters,
70
+ action.filter as GridFilterItem
71
+ ),
72
+ };
73
+ case DashboardReducerActionType.ResetFilters:
74
+ return { ...state, tableFilters: removeAllFilters() };
75
+ case DashboardReducerActionType.FilterTable:
76
+ return {
77
+ ...state,
78
+ tableFilters: addOrUpdateFilter(
79
+ state.tableFilters,
80
+ action.filter as GridFilterItem
81
+ ),
82
+ };
83
+
84
+ case DashboardReducerActionType.FilterTableBatch:
85
+ return {
86
+ ...state,
87
+ tableFilters: replaceFilters(action.filter!),
88
+ };
89
+
90
+ case DashboardReducerActionType.AppendFilterTableBatch:
91
+ return {
92
+ ...state,
93
+ tableFilters: addOrUpdateFilters(
94
+ state.tableFilters,
95
+ action.filter as GridFilterItem[]
96
+ ),
97
+ };
98
+ }
99
+ };
@@ -0,0 +1,47 @@
1
+ import { ComponentProps, Dispatch, MutableRefObject, RefObject } from 'react';
2
+ import { GridFilterItem } from '@redsift/table';
3
+ import { GridApi } from '@mui/x-data-grid-pro';
4
+
5
+ import { JSONArray } from '../../types';
6
+
7
+ /**
8
+ * Context props.
9
+ */
10
+ export type DashboardContextProps = {
11
+ dashboardRef?: RefObject<HTMLDivElement>;
12
+ data: JSONArray;
13
+ dataGridApiRef?: MutableRefObject<GridApi>;
14
+ dispatch: Dispatch<DashboardReducerAction>;
15
+ state: DashboardReducerState;
16
+ toggleUpdateContext?: () => void;
17
+ };
18
+
19
+ /**
20
+ * Reducer props.
21
+ */
22
+ export type DashboardReducerState = {
23
+ tableFilters: GridFilterItem[];
24
+ };
25
+
26
+ export enum DashboardReducerActionType {
27
+ ResetFilter = 'reset-filter',
28
+ ResetFilters = 'reset-filters',
29
+ FilterTable = 'filter-table',
30
+ FilterTableBatch = 'filter-table-batch',
31
+ AppendFilterTableBatch = 'append-filter-table-batch',
32
+ }
33
+
34
+ export type DashboardReducerAction = {
35
+ type: DashboardReducerActionType;
36
+ filter?: GridFilterItem | GridFilterItem[];
37
+ };
38
+
39
+ /**
40
+ * Component props.
41
+ */
42
+ export interface DashboardProps extends ComponentProps<'div'> {
43
+ /** Dataset that will be share across every components within the dashboard. */
44
+ data: JSONArray;
45
+ /** Datagrid API Ref. */
46
+ dataGridApiRef?: MutableRefObject<GridApi>;
47
+ }
@@ -0,0 +1,203 @@
1
+ // istanbul ignore file
2
+
3
+ import React from 'react';
4
+ import { Document, Image, Page, Text, View } from '@react-pdf/renderer';
5
+ import { GridValidRowModel } from '@mui/x-data-grid-pro';
6
+ import {
7
+ PdfTableColumn,
8
+ PdfTableRowProps,
9
+ PdfTableProps,
10
+ PdfDocumentProps,
11
+ PdfStyles,
12
+ } from './types';
13
+ import { getPdfStyles } from './styles';
14
+
15
+ const DEFAULT_COLUMN_WIDTH = 100;
16
+
17
+ const getWidthColumn = (width: number, totalW: number, nrColumns: number) => {
18
+ // calculation width column where the 7px margin between the columns is included
19
+ return `${Math.round((width * 100) / (totalW - 7 * (nrColumns - 1)))}%`;
20
+ };
21
+
22
+ const PdfTableRow = ({
23
+ rowIndex,
24
+ rowData,
25
+ columns,
26
+ styles,
27
+ totalWidth,
28
+ }: PdfTableRowProps) => {
29
+ return (
30
+ <View style={styles.tableRowContainer} key={`row-${rowIndex}`}>
31
+ {columns.map((column: PdfTableColumn, index: number) => {
32
+ const { field, width } = column;
33
+ let totalW = totalWidth;
34
+ let nrColumns = columns.length;
35
+
36
+ // The checkbox in the table will not be printed
37
+ if (field === '__check__') {
38
+ totalW = totalW - (width || 50);
39
+ nrColumns = nrColumns - 1;
40
+ return;
41
+ }
42
+ const widthColumn = getWidthColumn(
43
+ width || DEFAULT_COLUMN_WIDTH,
44
+ totalW,
45
+ nrColumns
46
+ );
47
+
48
+ // Empty value will print '-'
49
+ if (!rowData[field]) {
50
+ return (
51
+ <View
52
+ style={{
53
+ width: widthColumn,
54
+ paddingVertical: 2,
55
+ marginRight: 7,
56
+ }}
57
+ key={`cell-${rowIndex}-${index}`}
58
+ >
59
+ <Text style={styles.tableCellText}>{'-'}</Text>
60
+ </View>
61
+ );
62
+ }
63
+ return (
64
+ <View
65
+ style={{
66
+ width: widthColumn,
67
+ paddingVertical: 2,
68
+ marginRight: 7,
69
+ }}
70
+ key={`cell-${rowIndex}-${index}`}
71
+ >
72
+ <Text style={styles.tableCellText}>
73
+ {Array.isArray(rowData[field])
74
+ ? rowData[field].join(', ')
75
+ : rowData[field]}
76
+ </Text>
77
+ </View>
78
+ );
79
+ })}
80
+ </View>
81
+ );
82
+ };
83
+
84
+ const PdfTable = ({ dataTable, styles, localeText }: PdfTableProps) => {
85
+ const { data, columns, totalWidth } = dataTable;
86
+
87
+ return (
88
+ <>
89
+ <View style={styles.tableHeaderContainer}>
90
+ {columns.map((column: PdfTableColumn) => {
91
+ let totalW = totalWidth;
92
+ let nrColumns = columns.length;
93
+ const { field, headerName, width } = column;
94
+
95
+ // The checkbox in the table will not be printed
96
+ if (field === '__check__') {
97
+ totalW = totalW - (width || 50);
98
+ nrColumns = nrColumns - 1;
99
+ return;
100
+ }
101
+ const widthColumn = getWidthColumn(
102
+ width || DEFAULT_COLUMN_WIDTH,
103
+ totalW,
104
+ nrColumns
105
+ );
106
+
107
+ return (
108
+ <View
109
+ style={{
110
+ width: widthColumn,
111
+ paddingVertical: 2,
112
+ marginRight: 7,
113
+ }}
114
+ key={`heading-${field}`}
115
+ >
116
+ <Text style={styles.tableCellHeaderText}>{headerName}</Text>
117
+ </View>
118
+ );
119
+ })}
120
+ </View>
121
+ <View>
122
+ {data.map((row: GridValidRowModel, index: number) => {
123
+ if (index < 1000) {
124
+ return (
125
+ <PdfTableRow
126
+ key={index}
127
+ rowIndex={index}
128
+ rowData={data[index]}
129
+ columns={columns}
130
+ styles={styles}
131
+ totalWidth={totalWidth}
132
+ />
133
+ );
134
+ }
135
+ })}
136
+ </View>
137
+ {data.length >= 1000 ? (
138
+ <View style={styles.constraintsContainer}>
139
+ <Text style={styles.constraints}>
140
+ {localeText?.maxSizeDisclaimer ||
141
+ 'Due to processing constraints this pdf is limited to the first 1000 rows of data.'}
142
+ </Text>
143
+ </View>
144
+ ) : null}
145
+ </>
146
+ );
147
+ };
148
+
149
+ const Pagination = ({ styles }: { styles: PdfStyles }) => {
150
+ return (
151
+ <Text
152
+ style={styles.pageNumber}
153
+ render={({ pageNumber, totalPages }) => `${pageNumber}/${totalPages}`}
154
+ fixed
155
+ />
156
+ );
157
+ };
158
+
159
+ export const PdfDocument = ({
160
+ dashboardImage,
161
+ introduction,
162
+ localeText,
163
+ logo,
164
+ dataTable,
165
+ primaryColor,
166
+ }: PdfDocumentProps) => {
167
+ const styles = getPdfStyles(primaryColor);
168
+
169
+ return (
170
+ <Document>
171
+ <Page size="A4" style={styles.page}>
172
+ <>
173
+ {logo ? (
174
+ <View style={styles.logoContainer}>
175
+ <Image src={logo} style={styles.logo} />
176
+ </View>
177
+ ) : null}
178
+ {introduction ? (
179
+ <View style={styles.introductionContainer}>
180
+ <Text style={styles.introductionText}>{introduction}</Text>
181
+ </View>
182
+ ) : null}
183
+ {dashboardImage ? (
184
+ <View style={styles.dashboardImageContainer}>
185
+ <Image src={dashboardImage as unknown as string} />
186
+ </View>
187
+ ) : null}
188
+ {dataTable ? (
189
+ <View style={styles.tableContainer}>
190
+ <PdfTable
191
+ dataTable={dataTable}
192
+ styles={styles}
193
+ localeText={localeText}
194
+ />
195
+ </View>
196
+ ) : null}
197
+
198
+ <Pagination styles={styles} />
199
+ </>
200
+ </Page>
201
+ </Document>
202
+ );
203
+ };
@@ -0,0 +1,168 @@
1
+ // istanbul ignore file
2
+
3
+ import React, {
4
+ forwardRef,
5
+ JSXElementConstructor,
6
+ ReactElement,
7
+ RefObject,
8
+ useContext,
9
+ useEffect,
10
+ useState,
11
+ } from 'react';
12
+ import {
13
+ gridFilteredSortedRowEntriesSelector,
14
+ gridVisibleColumnDefinitionsSelector,
15
+ gridColumnsTotalWidthSelector,
16
+ } from '@mui/x-data-grid-pro';
17
+ import classNames from 'classnames';
18
+ import { saveAs } from 'file-saver';
19
+ import { pdf } from '@react-pdf/renderer';
20
+ import domToImage from 'dom-to-image';
21
+
22
+ import {
23
+ Comp,
24
+ Button,
25
+ RedsiftColorDefaultPrimary,
26
+ Spinner,
27
+ } from '@redsift/design-system';
28
+
29
+ import { PdfExportButtonProps } from './types';
30
+ import { PdfDocument } from './PdfDocument';
31
+ import { DashboardContext } from '../Dashboard';
32
+
33
+ const COMPONENT_NAME = 'PdfExportButton';
34
+ const CLASSNAME = 'redsift-pdf-export-button';
35
+
36
+ const getDashboardImage = async (
37
+ componentRef: RefObject<HTMLElement>
38
+ ): Promise<string> => {
39
+ const filter = (el: Node) => {
40
+ const classList = (el as HTMLElement).classList;
41
+ return !(
42
+ classList?.contains('redsift-data-grid') ||
43
+ classList?.contains('redsift-button')
44
+ );
45
+ };
46
+
47
+ const dashboardHeight = (
48
+ componentRef.current as HTMLElement
49
+ ).getBoundingClientRect().height;
50
+ const datagridHeight =
51
+ (componentRef.current as HTMLElement)
52
+ ?.getElementsByClassName('redsift-data-grid')?.[0]
53
+ ?.getBoundingClientRect()?.height || 0;
54
+
55
+ return new Promise((resolve) => {
56
+ domToImage
57
+ .toPng(componentRef.current as HTMLElement, {
58
+ filter,
59
+ height: dashboardHeight - datagridHeight,
60
+ })
61
+ .then(function (dataUrl: string) {
62
+ resolve(dataUrl);
63
+ })
64
+ .catch(() => {
65
+ resolve('');
66
+ });
67
+ });
68
+ };
69
+
70
+ export const PdfExportButton: Comp<PdfExportButtonProps, HTMLButtonElement> =
71
+ forwardRef((props, ref) => {
72
+ const {
73
+ children,
74
+ className,
75
+ componentRef: propComponentRef,
76
+ fileName,
77
+ introduction,
78
+ localeText,
79
+ logo,
80
+ onClick,
81
+ primaryColor,
82
+ ...forwardedProps
83
+ } = props;
84
+ const [componentRef, setComponentRef] = useState(propComponentRef);
85
+ const [isLoading, setIsLoading] = useState(false);
86
+
87
+ const { dashboardRef, dataGridApiRef } = useContext(DashboardContext);
88
+
89
+ useEffect(() => {
90
+ if (!componentRef || !componentRef.current) {
91
+ setComponentRef(dashboardRef);
92
+ }
93
+ }, [dashboardRef]);
94
+
95
+ const exportPdf = async () => {
96
+ if (onClick) {
97
+ onClick();
98
+ }
99
+ setIsLoading(true);
100
+ try {
101
+ const dashboardImage = await getDashboardImage(
102
+ componentRef || dashboardRef!
103
+ );
104
+
105
+ let dataTable;
106
+ if (
107
+ dataGridApiRef &&
108
+ dataGridApiRef.current &&
109
+ Object.keys(dataGridApiRef.current).length
110
+ ) {
111
+ dataTable = {
112
+ data: gridFilteredSortedRowEntriesSelector(
113
+ dataGridApiRef.current.state,
114
+ dataGridApiRef.current.instanceId
115
+ )
116
+ .slice(0, 1000)
117
+ .map(({ model }) => model),
118
+ columns: gridVisibleColumnDefinitionsSelector(
119
+ dataGridApiRef.current.state,
120
+ dataGridApiRef.current.instanceId
121
+ ),
122
+ totalWidth: gridColumnsTotalWidthSelector(
123
+ dataGridApiRef.current.state,
124
+ dataGridApiRef.current.instanceId
125
+ ),
126
+ };
127
+ }
128
+
129
+ const doc = (
130
+ <PdfDocument
131
+ localeText={localeText}
132
+ dashboardImage={dashboardImage}
133
+ introduction={introduction}
134
+ logo={logo}
135
+ primaryColor={primaryColor || RedsiftColorDefaultPrimary}
136
+ dataTable={dataTable}
137
+ />
138
+ );
139
+ const asPdf = pdf(
140
+ [] as unknown as ReactElement<
141
+ any,
142
+ string | JSXElementConstructor<any>
143
+ >
144
+ );
145
+ asPdf.updateContainer(doc);
146
+ const blob = await asPdf.toBlob();
147
+ saveAs(blob, fileName || 'redsift-dashboard.pdf');
148
+ } catch (e) {
149
+ console.log('error:', e);
150
+ }
151
+ setIsLoading(false);
152
+ };
153
+
154
+ return (
155
+ <Button
156
+ className={classNames(PdfExportButton.className, className)}
157
+ onClick={exportPdf}
158
+ isDisabled={isLoading}
159
+ {...forwardedProps}
160
+ ref={ref as RefObject<HTMLButtonElement>}
161
+ >
162
+ {isLoading ? <Spinner size="xsmall" color="no-data" /> : null}{' '}
163
+ {children}
164
+ </Button>
165
+ );
166
+ });
167
+ PdfExportButton.className = CLASSNAME;
168
+ PdfExportButton.displayName = COMPONENT_NAME;
@@ -0,0 +1,3 @@
1
+ // istanbul ignore file
2
+
3
+ export * from './PdfExportButton';
@@ -0,0 +1,151 @@
1
+ // istanbul ignore file
2
+
3
+ import { Font, StyleSheet } from '@react-pdf/renderer';
4
+ const BACKGROUND_COLOR = '#F8F8F8';
5
+ const GREY_1 = '#E2E6EA';
6
+ const GREY_2 = '#bff0fd';
7
+
8
+ export const getPdfStyles = (primaryColor: string) => {
9
+ Font.register({
10
+ family: 'Source Code Pro',
11
+ fonts: [
12
+ {
13
+ src: 'https://fonts.gstatic.com/s/sourcecodepro/v7/HI_SiYsKILxRpg3hIP6sJ7fM7PqlM-vT.ttf',
14
+ },
15
+ ],
16
+ });
17
+
18
+ Font.register({
19
+ family: 'Roboto',
20
+ fonts: [
21
+ {
22
+ fontWeight: 700,
23
+ src: 'https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/fonts/roboto/Roboto-Regular.ttf',
24
+ },
25
+ {
26
+ fontWeight: 400,
27
+ src: 'https://cdnjs.cloudflare.com/ajax/libs/ink/3.1.10/fonts/Roboto/roboto-light-webfont.ttf',
28
+ },
29
+ ],
30
+ });
31
+
32
+ return StyleSheet.create({
33
+ page: {
34
+ borderTopStyle: 'solid',
35
+ borderTopWidth: 32,
36
+ borderTopColor: primaryColor,
37
+ borderBottomStyle: 'solid',
38
+ borderBottomWidth: 6,
39
+ borderBottomColor: primaryColor,
40
+ backgroundColor: BACKGROUND_COLOR,
41
+ paddingTop: 20,
42
+ paddingBottom: 40,
43
+ },
44
+ logoContainer: {
45
+ margin: 0,
46
+ paddingBottom: 10,
47
+ alignItems: 'center',
48
+ width: '100%',
49
+ },
50
+ logo: {
51
+ width: 'auto',
52
+ height: 30,
53
+ },
54
+ introductionContainer: {
55
+ lineHeight: 1.4,
56
+ textAlign: 'center',
57
+ marginHorizontal: 40,
58
+ marginVertical: 10,
59
+ },
60
+ introductionText: {
61
+ fontSize: 8,
62
+ fontFamily: 'Roboto',
63
+ fontWeight: 400,
64
+ color: 'black',
65
+ },
66
+ pageNumber: {
67
+ fontFamily: 'Source Code Pro',
68
+ position: 'absolute',
69
+ fontSize: 6,
70
+ bottom: 5,
71
+ right: 20,
72
+ left: 0,
73
+ textAlign: 'right',
74
+ color: 'black',
75
+ },
76
+ pageContinue: {
77
+ fontSize: 8,
78
+ fontFamily: 'Roboto',
79
+ fontWeight: 400,
80
+ top: -10,
81
+ left: 20,
82
+ },
83
+ tableContainer: {
84
+ backgroundColor: 'white',
85
+ marginHorizontal: 20,
86
+ marginVertical: 10,
87
+ paddingTop: 10,
88
+ paddingLeft: 10,
89
+ borderRightWidth: 1,
90
+ borderBottomWidth: 1,
91
+ borderRightColor: BACKGROUND_COLOR,
92
+ borderBottomColor: GREY_1,
93
+ width: 'auto',
94
+ },
95
+ tableRowContainer: {
96
+ flexDirection: 'row',
97
+ borderBottomColor: GREY_2,
98
+ alignItems: 'flex-start',
99
+ fontSize: 8,
100
+ marginLeft: 16,
101
+ marginRight: 24,
102
+ height: 'auto',
103
+ },
104
+ tableHeaderContainer: {
105
+ flexDirection: 'row',
106
+ alignItems: 'flex-start',
107
+ fontSize: 7,
108
+ fontFamily: 'Roboto',
109
+ fontWeight: 700,
110
+ marginLeft: 16,
111
+ marginRight: 24,
112
+ marginBottom: 12,
113
+ paddingTop: 12,
114
+ paddingBottom: 12,
115
+ borderBottomWidth: 2,
116
+ borderBottomColor: primaryColor,
117
+ },
118
+ tableCellHeaderText: {
119
+ fontSize: 7,
120
+ fontFamily: 'Roboto',
121
+ fontWeight: 700,
122
+ color: 'black',
123
+ overflow: 'hidden',
124
+ },
125
+ tableCellText: {
126
+ fontSize: 6,
127
+ lineHeight: 1.5,
128
+ fontFamily: 'Roboto',
129
+ fontWeight: 400,
130
+ color: 'black',
131
+ overflow: 'hidden',
132
+ },
133
+ dashboardImageContainer: {
134
+ marginHorizontal: 20,
135
+ marginTop: 0,
136
+ marginBottom: 10,
137
+ },
138
+ constraintsContainer: {
139
+ width: 'auto',
140
+ height: 'auto',
141
+ paddingVertical: 30,
142
+ textAlign: 'center',
143
+ },
144
+ constraints: {
145
+ fontSize: 6,
146
+ fontFamily: 'Roboto',
147
+ fontWeight: 700,
148
+ color: 'black',
149
+ },
150
+ });
151
+ };