dce-reactkit 1.0.20 → 2.0.2

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 (215) hide show
  1. package/.eslintrc.js +95 -0
  2. package/lib/components/LoadingSpinner.d.ts +6 -0
  3. package/lib/components/LoadingSpinner.js +41 -0
  4. package/lib/components/LoadingSpinner.js.map +1 -0
  5. package/lib/components/Modal.d.ts +69 -0
  6. package/lib/components/Modal.js +315 -0
  7. package/lib/components/Modal.js.map +1 -0
  8. package/lib/errors/ErrorWithCode.d.ts +9 -0
  9. package/lib/errors/ErrorWithCode.js +33 -0
  10. package/lib/errors/ErrorWithCode.js.map +1 -0
  11. package/lib/helpers/abbreviate.d.ts +9 -0
  12. package/lib/helpers/abbreviate.js +21 -0
  13. package/lib/helpers/abbreviate.js.map +1 -0
  14. package/lib/helpers/avg.d.ts +8 -0
  15. package/lib/helpers/avg.js +24 -0
  16. package/lib/helpers/avg.js.map +1 -0
  17. package/lib/helpers/ceilToNumDecimals.d.ts +9 -0
  18. package/lib/helpers/ceilToNumDecimals.js +15 -0
  19. package/lib/helpers/ceilToNumDecimals.js.map +1 -0
  20. package/lib/helpers/floorToNumDecimals.d.ts +9 -0
  21. package/lib/helpers/floorToNumDecimals.js +15 -0
  22. package/lib/helpers/floorToNumDecimals.js.map +1 -0
  23. package/lib/helpers/forceNumIntoBounds.d.ts +10 -0
  24. package/lib/helpers/forceNumIntoBounds.js +15 -0
  25. package/lib/helpers/forceNumIntoBounds.js.map +1 -0
  26. package/lib/helpers/padDecimalZeros.d.ts +10 -0
  27. package/lib/helpers/padDecimalZeros.js +30 -0
  28. package/lib/helpers/padDecimalZeros.js.map +1 -0
  29. package/lib/helpers/padZerosLeft.d.ts +9 -0
  30. package/lib/helpers/padZerosLeft.js +21 -0
  31. package/lib/helpers/padZerosLeft.js.map +1 -0
  32. package/lib/helpers/roundToNumDecimals.d.ts +9 -0
  33. package/lib/helpers/roundToNumDecimals.js +15 -0
  34. package/lib/helpers/roundToNumDecimals.js.map +1 -0
  35. package/lib/helpers/sum.d.ts +8 -0
  36. package/lib/helpers/sum.js +15 -0
  37. package/lib/helpers/sum.js.map +1 -0
  38. package/lib/helpers/visitServerEndpoint.d.ts +17 -0
  39. package/lib/helpers/visitServerEndpoint.js +84 -0
  40. package/lib/helpers/visitServerEndpoint.js.map +1 -0
  41. package/lib/helpers/waitMs.d.ts +7 -0
  42. package/lib/helpers/waitMs.js +55 -0
  43. package/lib/helpers/waitMs.js.map +1 -0
  44. package/lib/types/ReactKitErrorCode.d.ts +9 -0
  45. package/lib/types/ReactKitErrorCode.js +14 -0
  46. package/lib/types/ReactKitErrorCode.js.map +1 -0
  47. package/lib/types/Variant.d.ts +15 -0
  48. package/lib/types/Variant.js +19 -0
  49. package/lib/types/Variant.js.map +1 -0
  50. package/package.json +22 -40
  51. package/src/components/LoadingSpinner.tsx +105 -0
  52. package/src/components/Modal.tsx +447 -0
  53. package/src/errors/ErrorWithCode.tsx +15 -0
  54. package/src/helpers/abbreviate.tsx +23 -0
  55. package/src/helpers/avg.tsx +22 -0
  56. package/src/helpers/ceilToNumDecimals.tsx +13 -0
  57. package/src/helpers/floorToNumDecimals.tsx +13 -0
  58. package/src/helpers/forceNumIntoBounds.tsx +19 -0
  59. package/src/helpers/padDecimalZeros.tsx +32 -0
  60. package/src/helpers/padZerosLeft.tsx +21 -0
  61. package/src/helpers/roundToNumDecimals.tsx +13 -0
  62. package/src/helpers/sum.tsx +16 -0
  63. package/src/helpers/visitServerEndpoint.tsx +59 -0
  64. package/src/helpers/waitMs.tsx +12 -0
  65. package/src/types/ReactKitErrorCode.tsx +12 -0
  66. package/src/types/Variant.tsx +16 -0
  67. package/tsconfig.json +24 -0
  68. package/index.js +0 -1
  69. package/lib/Bar.js +0 -169
  70. package/lib/Donut.js +0 -145
  71. package/lib/DownloadButton.js +0 -81
  72. package/lib/Histogram.js +0 -193
  73. package/lib/Scatter.js +0 -162
  74. package/lib/StackedBar.js +0 -243
  75. package/lib/common/chart/Holder/index.js +0 -182
  76. package/lib/common/chart/Holder/style.css +0 -41
  77. package/lib/common/chart/colors.js +0 -12
  78. package/lib/common/csvToString.js +0 -23
  79. package/lib/common/isFloat.js +0 -14
  80. package/lib/common/isInteger.js +0 -24
  81. package/lib/common/math.js +0 -21
  82. package/lib/common/validators.js +0 -18
  83. package/lib/glyph/Adjust.js +0 -13
  84. package/lib/glyph/Angle.js +0 -44
  85. package/lib/glyph/Archive.js +0 -13
  86. package/lib/glyph/Arrow.js +0 -49
  87. package/lib/glyph/ArrowsHorizontal.js +0 -13
  88. package/lib/glyph/ArrowsVertical.js +0 -13
  89. package/lib/glyph/Award.js +0 -13
  90. package/lib/glyph/Ban.js +0 -13
  91. package/lib/glyph/Barcode.js +0 -13
  92. package/lib/glyph/Bed.js +0 -13
  93. package/lib/glyph/Bell.js +0 -35
  94. package/lib/glyph/Bolt.js +0 -13
  95. package/lib/glyph/Bookmark.js +0 -37
  96. package/lib/glyph/Bug.js +0 -13
  97. package/lib/glyph/Calendar.js +0 -13
  98. package/lib/glyph/Check.js +0 -36
  99. package/lib/glyph/Checkbox.js +0 -40
  100. package/lib/glyph/Chevron.js +0 -63
  101. package/lib/glyph/Circle.js +0 -15
  102. package/lib/glyph/Cog.js +0 -13
  103. package/lib/glyph/DoubleAngle.js +0 -45
  104. package/lib/glyph/Download.js +0 -13
  105. package/lib/glyph/Ellipsis.js +0 -36
  106. package/lib/glyph/Envelope.js +0 -13
  107. package/lib/glyph/File.js +0 -37
  108. package/lib/glyph/IdCard.js +0 -13
  109. package/lib/glyph/Menu.js +0 -13
  110. package/lib/glyph/Minus.js +0 -13
  111. package/lib/glyph/Move.js +0 -13
  112. package/lib/glyph/Plus.js +0 -13
  113. package/lib/glyph/Square.js +0 -47
  114. package/lib/glyph/Teacher.js +0 -13
  115. package/lib/glyph/TextAlign.js +0 -53
  116. package/lib/glyph/Times.js +0 -36
  117. package/lib/glyph/WarningTriangle.js +0 -13
  118. package/lib/index.js +0 -1
  119. package/src-old/common/csvToString.js +0 -5
  120. package/src-old/common/isFloat.js +0 -3
  121. package/src-old/common/isInteger.js +0 -14
  122. package/src-old/common/math.js +0 -16
  123. package/src-old/common/validators.js +0 -14
  124. package/src-old/components/Alert.js +0 -79
  125. package/src-old/components/Button.js +0 -64
  126. package/src-old/components/CSV/autoMatch.js +0 -174
  127. package/src-old/components/CSV/getNumCols.js +0 -15
  128. package/src-old/components/CSV/index.js +0 -743
  129. package/src-old/components/Card.js +0 -64
  130. package/src-old/components/Chart/Bar.js +0 -169
  131. package/src-old/components/Chart/Donut.js +0 -160
  132. package/src-old/components/Chart/Histogram.js +0 -189
  133. package/src-old/components/Chart/Scatter.js +0 -162
  134. package/src-old/components/Chart/colors.js +0 -20
  135. package/src-old/components/Checkbox/index.js +0 -106
  136. package/src-old/components/Checkbox/style.css +0 -11
  137. package/src-old/components/DatePicker.js +0 -59
  138. package/src-old/components/DownloadButton.js +0 -72
  139. package/src-old/components/Dropdown.js +0 -120
  140. package/src-old/components/Layout/LeftAddon.js +0 -63
  141. package/src-old/components/Layout/RightAddon.js +0 -64
  142. package/src-old/components/Layout/TwoCol.js +0 -67
  143. package/src-old/components/Modal.js +0 -161
  144. package/src-old/components/PhotoRoster.js +0 -152
  145. package/src-old/components/Pills.js +0 -117
  146. package/src-old/components/Radio.js +0 -94
  147. package/src-old/components/Splash/Error.js +0 -62
  148. package/src-old/components/Splash/Load/index.js +0 -32
  149. package/src-old/components/Splash/Load/style.css +0 -51
  150. package/src-old/components/Stat/General.js +0 -252
  151. package/src-old/components/Stat/helpers/calcAverage.js +0 -20
  152. package/src-old/components/Stat/helpers/calcExtremes.js +0 -27
  153. package/src-old/components/Stat/helpers/calcMedian.js +0 -32
  154. package/src-old/components/Stat/helpers/calcStdDev.js +0 -30
  155. package/src-old/components/Stat/helpers/calcSum.js +0 -8
  156. package/src-old/components/UserProfile/index.js +0 -151
  157. package/src-old/components/UserProfile/style.css +0 -33
  158. package/src-old/components/Well.js +0 -28
  159. package/src-old/components/Wizard/Question/CSV.js +0 -307
  160. package/src-old/components/Wizard/Question/Chooser/Assignment.js +0 -108
  161. package/src-old/components/Wizard/Question/Chooser/Course.js +0 -214
  162. package/src-old/components/Wizard/Question/Chooser/helpers/divideIntoSections.js +0 -16
  163. package/src-old/components/Wizard/Question/Chooser/helpers/genGetItemsPromise.js +0 -20
  164. package/src-old/components/Wizard/Question/Chooser/helpers/preprocessSections.js +0 -36
  165. package/src-old/components/Wizard/Question/Chooser/index.js +0 -306
  166. package/src-old/components/Wizard/Question/Form/Field/CheckableItem.js +0 -155
  167. package/src-old/components/Wizard/Question/Form/Field/Input.js +0 -149
  168. package/src-old/components/Wizard/Question/Form/Field/NumberItem.js +0 -374
  169. package/src-old/components/Wizard/Question/Form/Field/index.js +0 -34
  170. package/src-old/components/Wizard/Question/Form/Field/readme.md +0 -7
  171. package/src-old/components/Wizard/Question/Form/index.js +0 -175
  172. package/src-old/components/Wizard/Question/Form/style.css +0 -3
  173. package/src-old/components/Wizard/Question/OneButton.js +0 -80
  174. package/src-old/components/Wizard/Question/index.js +0 -46
  175. package/src-old/components/Wizard/Question/readme.md +0 -10
  176. package/src-old/components/Wizard/QuestionGroup/AnswerAll.js +0 -126
  177. package/src-old/components/Wizard/QuestionGroup/AnswerAny.js +0 -60
  178. package/src-old/components/Wizard/index.js +0 -233
  179. package/src-old/components/Wizard/style.css +0 -21
  180. package/src-old/glyphs/Adjust.js +0 -13
  181. package/src-old/glyphs/Angle.js +0 -57
  182. package/src-old/glyphs/Archive.js +0 -13
  183. package/src-old/glyphs/Arrow.js +0 -85
  184. package/src-old/glyphs/ArrowsHorizontal.js +0 -13
  185. package/src-old/glyphs/ArrowsVertical.js +0 -13
  186. package/src-old/glyphs/Award.js +0 -13
  187. package/src-old/glyphs/Ban.js +0 -13
  188. package/src-old/glyphs/Barcode.js +0 -13
  189. package/src-old/glyphs/Bed.js +0 -13
  190. package/src-old/glyphs/Bell.js +0 -34
  191. package/src-old/glyphs/Bolt.js +0 -13
  192. package/src-old/glyphs/Bookmark.js +0 -36
  193. package/src-old/glyphs/Bug.js +0 -13
  194. package/src-old/glyphs/Calendar.js +0 -13
  195. package/src-old/glyphs/Check.js +0 -34
  196. package/src-old/glyphs/Checkbox.js +0 -42
  197. package/src-old/glyphs/Chevron.js +0 -88
  198. package/src-old/glyphs/Circle.js +0 -16
  199. package/src-old/glyphs/Cog.js +0 -13
  200. package/src-old/glyphs/DoubleAngle.js +0 -58
  201. package/src-old/glyphs/Download.js +0 -13
  202. package/src-old/glyphs/Ellipsis.js +0 -35
  203. package/src-old/glyphs/Envelope.js +0 -13
  204. package/src-old/glyphs/File.js +0 -40
  205. package/src-old/glyphs/IdCard.js +0 -13
  206. package/src-old/glyphs/Menu.js +0 -13
  207. package/src-old/glyphs/Minus.js +0 -13
  208. package/src-old/glyphs/Move.js +0 -13
  209. package/src-old/glyphs/Plus.js +0 -13
  210. package/src-old/glyphs/Square.js +0 -48
  211. package/src-old/glyphs/Teacher.js +0 -13
  212. package/src-old/glyphs/TextAlign.js +0 -58
  213. package/src-old/glyphs/Times.js +0 -34
  214. package/src-old/glyphs/WarningTriangle.js +0 -13
  215. package/src-old/index.js +0 -1
@@ -1,743 +0,0 @@
1
- import React, { Component } from 'react';
2
- import PropTypes from 'prop-types';
3
- import genUUID from 'uuid/v1';
4
-
5
- // Import bootstrap componenets
6
- import { Table } from 'reactstrap';
7
- // Import bootstrap stylesheet
8
- import 'bootstrap/dist/css/bootstrap.min.css';
9
-
10
- // Import other components
11
- import Alert from '../Alert';
12
- import LeftAddon from '../Layout/LeftAddon';
13
- import RightAddon from '../Layout/RightAddon';
14
- import Button from '../Button';
15
- import Dropdown from '../Dropdown';
16
- import DownloadButton from '../DownloadButton';
17
-
18
- // Import glyphs
19
- import Ellipsis from '../../glyphs/Ellipsis';
20
- import Times from '../../glyphs/Times';
21
- import Chevron from '../../glyphs/Chevron';
22
- import Checkbox from '../../glyphs/Checkbox';
23
- import Check from '../../glyphs/Check';
24
-
25
- // Import helper functions
26
- import autoMatch from './autoMatch';
27
- import getNumCols from './getNumCols';
28
- // Import script for stringifying csv for download
29
- import csvToString from '../../common/csvToString';
30
-
31
- class CSV extends Component {
32
- constructor(props) {
33
- super(props);
34
-
35
- // Pre-process CSV to determine which columns can be matched (use validator)
36
- const {
37
- headersToMatch,
38
- csv,
39
- } = this.props;
40
-
41
- // Skip processing if we have no work to do
42
- const noCSV = (
43
- !csv
44
- || !csv.data
45
- || csv.data.length === 0
46
- );
47
- if (!headersToMatch || noCSV) {
48
- this.state = {
49
- noCSV,
50
- };
51
- return;
52
- }
53
-
54
- // Perform auto-matching
55
- const {
56
- allowedHeaders,
57
- columnMapping,
58
- } = autoMatch(csv, headersToMatch);
59
- this.allowedHeaders = allowedHeaders;
60
-
61
- // Save headerKey map for easier lookup
62
- this.headerKeyToHeader = {};
63
- headersToMatch.forEach((header) => {
64
- this.headerKeyToHeader[header.key] = header;
65
- });
66
-
67
- // Save boolean (auto matching occurred)
68
- this.autoMatchingOccurred = Object.keys(columnMapping).length > 0;
69
-
70
- // Save state
71
- this.state = {
72
- columnMapping,
73
- };
74
-
75
- // Bind functions
76
- this.matchSelected = this.matchSelected.bind(this);
77
- this.onDoneMatchingClicked = this.onDoneMatchingClicked.bind(this);
78
- this.unmatch = this.unmatch.bind(this);
79
- }
80
-
81
- onDoneMatchingClicked() {
82
- // Deconstruct props
83
- const {
84
- csv,
85
- onFinished,
86
- } = this.props;
87
-
88
- // Skip if no listener
89
- if (!onFinished) {
90
- return;
91
- }
92
-
93
- // Deconstruct state
94
- const { columnMapping } = this.state;
95
-
96
- // Post-process csv
97
- const headerKeyToColumnIndex = {};
98
- Object.keys(columnMapping).forEach((cIndex) => {
99
- headerKeyToColumnIndex[columnMapping[cIndex]] = cIndex;
100
- });
101
- const matchedHeaderKeys = Object.values(columnMapping);
102
- // Process each row, turning it into an object (with headerKeys as props)
103
- const rowObjects = csv.data.map((datum) => {
104
- const rowObject = {};
105
- matchedHeaderKeys.forEach((headerKey) => {
106
- const cIndex = headerKeyToColumnIndex[headerKey];
107
- const datumKey = (
108
- csv.headers
109
- ? csv.headers[cIndex]
110
- : cIndex
111
- );
112
- rowObject[headerKey] = datum[datumKey];
113
- });
114
- return rowObject;
115
- });
116
- const newCSV = {
117
- headers: matchedHeaderKeys,
118
- data: rowObjects,
119
- };
120
-
121
- onFinished(newCSV);
122
- }
123
-
124
- unmatch(cIndex) {
125
- // Get previous column mapping
126
- const { columnMapping } = this.state;
127
-
128
- // Update column mapping
129
- delete columnMapping[cIndex];
130
-
131
- // Save state
132
- this.setState({
133
- columnMapping,
134
- });
135
- }
136
-
137
- matchSelected(cIndex, headerKey) {
138
- // Get previous column mapping
139
- const { columnMapping } = this.state;
140
-
141
- // Unmatch other column that are matched with this headerKey (if applicable)
142
- const cIndices = Object.keys(columnMapping);
143
- for (let i = 0; i < cIndices.length; i++) {
144
- if (columnMapping[cIndices[i]] === headerKey) {
145
- // Unmatch this column
146
- delete columnMapping[cIndices[i]];
147
- break;
148
- }
149
- }
150
-
151
- // Save new mapping
152
- columnMapping[cIndex] = headerKey;
153
- this.setState({
154
- columnMapping,
155
- changesMade: true,
156
- });
157
- }
158
-
159
- render() {
160
- // Detect "no csv" error
161
- const { noCSV } = this.state;
162
- if (noCSV) {
163
- // No data
164
- return (
165
- <Alert
166
- color="warning"
167
- >
168
- <h3>This CSV file is empty</h3>
169
- Unfortunately, we can&apos;t load this CSV file because it was empty.
170
- </Alert>
171
- );
172
- }
173
-
174
- // Deconstruct properties
175
- const {
176
- csv,
177
- noHeader,
178
- numRowsToPreview,
179
- onReupload,
180
- headersToMatch,
181
- downloadFilename,
182
- } = this.props;
183
-
184
- // Get current header mapping
185
- const {
186
- columnMapping,
187
- impossible,
188
- changesMade,
189
- } = this.state;
190
-
191
- // Display error if matching is impossible
192
- if (impossible) {
193
- const requiredColumns = headersToMatch.map((header) => {
194
- return (
195
- <li key={genUUID()}>
196
- <strong>{header.title}</strong>
197
- {header.description && (
198
- <span>
199
- &nbsp;–&nbsp;
200
- <em>{header.description}</em>
201
- </span>
202
- )}
203
- </li>
204
- );
205
- });
206
- return (
207
- <Alert
208
- color="danger"
209
- className="text-center"
210
- >
211
- <h3>
212
- This file doesn&apos;t match our requirements
213
- </h3>
214
- You need a column for each of the following types of info:
215
- <div>
216
- <div className="text-left d-inline-block">
217
- <ul>
218
- {requiredColumns}
219
- </ul>
220
- </div>
221
- </div>
222
- <Button
223
- text={(
224
- <span>
225
- <Chevron direction="left" className="mr-2" />
226
- Back
227
- </span>
228
- )}
229
- onClick={onReupload}
230
- color="secondary"
231
- size="lg"
232
- />
233
- </Alert>
234
- );
235
- }
236
-
237
- // Determine the number of columns in the csv
238
- const numCols = getNumCols(csv);
239
-
240
- // Check if CSV has too few cols
241
- if (numCols === 0) {
242
- // No data
243
- return (
244
- <Alert
245
- color="warning"
246
- >
247
- <h2>This CSV file is empty (no columns).</h2>
248
- </Alert>
249
- );
250
- }
251
-
252
- // Create table header
253
- let tableHeaderContent = [];
254
-
255
- // > Add header matching selectors
256
- if (headersToMatch) {
257
- // Loop through each csv column and create a selector cell
258
- const selectors = [];
259
- for (let i = 0; i < numCols; i++) {
260
- const currentHeaderKey = columnMapping[i];
261
- const currentHeader = (
262
- currentHeaderKey
263
- ? this.headerKeyToHeader[currentHeaderKey]
264
- : null
265
- );
266
- // Create dropdown items
267
- const items = (
268
- this.allowedHeaders[i].map((header) => {
269
- // Skip current header
270
- if (header === currentHeader) {
271
- return null;
272
- }
273
- return {
274
- text: header.title,
275
- onClick: () => {
276
- this.matchSelected(i, header.key);
277
- },
278
- };
279
- }).filter((x) => { return x; })
280
- );
281
- if (currentHeader) {
282
- // Add "ignore" optiona
283
- items.splice(0, 0, {
284
- text: (
285
- <span>
286
- <Times className="mr-2" />
287
- Ignore this Column
288
- </span>
289
- ),
290
- onClick: () => {
291
- this.unmatch(i);
292
- },
293
- });
294
- }
295
-
296
- selectors.push((
297
- <th
298
- key={genUUID()}
299
- >
300
- <Dropdown
301
- title={(
302
- currentHeader
303
- ? currentHeader.title
304
- : 'Ignored'
305
- )}
306
- items={items}
307
- color={(
308
- currentHeader
309
- ? 'info'
310
- : 'secondary'
311
- )}
312
- block
313
- />
314
- </th>
315
- ));
316
- }
317
- // Add selectors as another row
318
- tableHeaderContent.push((
319
- <tr
320
- key={genUUID()}
321
- >
322
- {selectors}
323
- </tr>
324
- ));
325
- }
326
-
327
- // > Add csv headers
328
- if (!noHeader && csv.headers) {
329
- const headerElements = csv.headers.map((title) => {
330
- return (
331
- <th
332
- key={title}
333
- >
334
- {title}
335
- </th>
336
- );
337
- });
338
- tableHeaderContent.push((
339
- <tr
340
- key={genUUID()}
341
- >
342
- {headerElements}
343
- </tr>
344
- ));
345
- }
346
-
347
- // Create table rows
348
- let tableRowContent = [];
349
- for (let i = 0; i < (numRowsToPreview || csv.data.length); i++) {
350
- // Create cells
351
- let cells;
352
- if (csv.headers) {
353
- // Assume data is in object format
354
- cells = csv.headers.map((header) => {
355
- return (
356
- <td
357
- key={genUUID()}
358
- >
359
- {csv.data[i][header]}
360
- </td>
361
- );
362
- });
363
- } else {
364
- // Assume data is in array format
365
- cells = csv.data[i].map((cellText) => {
366
- return (
367
- <td
368
- key={genUUID()}
369
- >
370
- {cellText}
371
- </td>
372
- );
373
- });
374
- }
375
-
376
- tableRowContent.push((
377
- <tr
378
- key={genUUID()}
379
- >
380
- {cells}
381
- </tr>
382
- ));
383
- }
384
-
385
- // Add ellipses if preview cut off some rows
386
- if (numRowsToPreview && numRowsToPreview < csv.data.length) {
387
- const cells = [];
388
- for (let i = 0; i < numCols; i++) {
389
- cells.push((
390
- <td
391
- key={genUUID()}
392
- className="text-center"
393
- >
394
- <Ellipsis vertical />
395
- </td>
396
- ));
397
- }
398
- tableRowContent.push((
399
- <tr
400
- key={genUUID()}
401
- >
402
- {cells}
403
- </tr>
404
- ));
405
- }
406
-
407
- // Wrap header and row content objects
408
- tableRowContent = (
409
- tableRowContent.length > 0
410
- ? (
411
- <tbody>
412
- {tableRowContent}
413
- </tbody>
414
- )
415
- : null
416
- );
417
- tableHeaderContent = (
418
- tableHeaderContent.length > 0
419
- ? (
420
- <thead className="text-nowrap">
421
- {tableHeaderContent}
422
- </thead>
423
- )
424
- : null
425
- );
426
-
427
- // If performing header matching, create match message
428
- let matchMessage;
429
- if (headersToMatch) {
430
- // Create checklist
431
- const requiredElements = [];
432
- const optionalElements = [];
433
- let requirementsMet = true;
434
- let optionalColumnsMet = true;
435
- headersToMatch
436
- .forEach((header) => {
437
- const isMatched = (
438
- Object.values(columnMapping).indexOf(header.key) >= 0
439
- );
440
- const element = (
441
- <LeftAddon
442
- key={genUUID()}
443
- >
444
- <Checkbox
445
- checked={isMatched}
446
- className="mr-2"
447
- />
448
- <div
449
- className={(
450
- isMatched
451
- ? 'text-success'
452
- : 'text-danger'
453
- )}
454
- >
455
- {header.title}
456
- {header.description && (
457
- <span>
458
- &nbsp;–&nbsp;
459
- <em>{header.description}</em>
460
- </span>
461
- )}
462
- </div>
463
- </LeftAddon>
464
- );
465
- if (header.required) {
466
- requiredElements.push(element);
467
- if (!isMatched) {
468
- requirementsMet = false;
469
- }
470
- } else {
471
- optionalElements.push(element);
472
- if (!isMatched) {
473
- optionalColumnsMet = false;
474
- }
475
- }
476
- });
477
-
478
- // Create back button
479
- const backButton = (
480
- <Button
481
- text={(
482
- <span>
483
- <Chevron
484
- direction="left"
485
- className="mr-2"
486
- />
487
- Choose New File
488
- </span>
489
- )}
490
- color="secondary"
491
- size="lg"
492
- className="mt-2 mr-2"
493
- onClick={onReupload}
494
- />
495
- );
496
-
497
- // Display "please verify" if all columns are matched and auto-matching
498
- // occurred (an user hasn't made changes)
499
- if (
500
- requirementsMet
501
- && optionalColumnsMet
502
- && !changesMade
503
- && this.autoMatchingOccurred
504
- ) {
505
- const dropdownMessage = 'We auto-detected the columns of your CSV file. Verify that each dropdown matches the type of info in the column below it.';
506
- matchMessage = (
507
- <Alert
508
- color="warning"
509
- className="text-center pb-2"
510
- >
511
- <h4>
512
- Verify Our Matchings
513
- </h4>
514
- <div>
515
- {dropdownMessage}
516
- </div>
517
- {backButton}
518
- <Button
519
- text={(
520
- <span>
521
- <Check
522
- className="mr-2"
523
- />
524
- Looks Good
525
- </span>
526
- )}
527
- color="success"
528
- size="lg"
529
- className="mt-2"
530
- onClick={this.onDoneMatchingClicked}
531
- />
532
- </Alert>
533
- );
534
- } else {
535
- // Divide elements into required and optional
536
- const requiredChecklist = (
537
- requiredElements.length > 0
538
- ? (
539
- <div>
540
- <div className="text-dark">
541
- <strong>Required columns:</strong>
542
- </div>
543
- {requiredElements}
544
- </div>
545
- )
546
- : null
547
- );
548
- const optionalChecklist = (
549
- optionalElements.length > 0
550
- ? (
551
- <div>
552
- <div className="text-dark">
553
- <strong>Optional columns:</strong>
554
- </div>
555
- {optionalElements}
556
- </div>
557
- )
558
- : null
559
- );
560
-
561
- // Create done button
562
- const doneButton = (
563
- <Button
564
- text={(
565
- <span>
566
- <Check
567
- className="mr-2"
568
- />
569
- Done Matching
570
- </span>
571
- )}
572
- color="success"
573
- size="lg"
574
- className="mt-2"
575
- onClick={this.onDoneMatchingClicked}
576
- disabled={!requirementsMet}
577
- />
578
- );
579
- matchMessage = (
580
- <Alert
581
- color="warning"
582
- className="text-center pb-2"
583
- >
584
- <h4>
585
- Match Columns
586
- </h4>
587
- Use the dropdowns to tell us where the following columns are:
588
- {(this.autoMatchingOccurred && !changesMade) && (
589
- <div>
590
- (we tried to
591
- <em> auto-match </em>
592
- some of the columns for you)
593
- </div>
594
- )}
595
- <Alert
596
- color="light"
597
- className="mb-0 text-left"
598
- >
599
- {requiredChecklist}
600
- {optionalChecklist}
601
- </Alert>
602
- {backButton}
603
- {doneButton}
604
- </Alert>
605
- );
606
- }
607
- }
608
-
609
- // Create the csv preview
610
- const csvPreview = (
611
- <div
612
- style={{
613
- overflowX: 'scroll',
614
- backgroundColor: 'white',
615
- }}
616
- >
617
- <Table
618
- striped
619
- bordered
620
- className="mb-0"
621
- >
622
- {tableHeaderContent}
623
- {tableRowContent}
624
- </Table>
625
- </div>
626
- );
627
-
628
- // Download button
629
- let downloadButton;
630
- if (downloadFilename) {
631
- const filename = (
632
- downloadFilename.endsWith('.csv')
633
- ? downloadFilename
634
- : `${downloadFilename}.csv`
635
- );
636
- const fileContents = csvToString(csv);
637
- const downloadMessage = (
638
- <RightAddon centerVertically>
639
- <h3 className="mb-0">{filename}</h3>
640
- <DownloadButton
641
- filename={filename}
642
- contents={fileContents}
643
- large
644
- />
645
- </RightAddon>
646
- );
647
- if (matchMessage) {
648
- // Performing matching. Add an unobtrusive download button
649
- downloadButton = (
650
- <Alert
651
- color="dark"
652
- className="mb-0"
653
- >
654
- {downloadMessage}
655
- </Alert>
656
- );
657
- } else {
658
- // Not performing matching. Show the preview inside the download alert
659
- return (
660
- <Alert
661
- color="dark"
662
- className="mb-0"
663
- >
664
- {downloadMessage}
665
- <div className="mt-2">
666
- {csvPreview}
667
- </div>
668
- </Alert>
669
- );
670
- }
671
- }
672
-
673
- return (
674
- <div>
675
- {downloadButton}
676
- {matchMessage}
677
- {csvPreview}
678
- </div>
679
- );
680
- }
681
- }
682
-
683
- CSV.propTypes = {
684
- /* The CSV to show. Note: csv.headers and csv.data have different forms,
685
- * depending upon the value of noHeader */
686
- csv: PropTypes.shape({
687
- headers: PropTypes.arrayOf(PropTypes.string),
688
- data: PropTypes.arrayOf(PropTypes.object),
689
- }).isRequired,
690
- // Headers to match (matching is performed if headers are included)
691
- headersToMatch: PropTypes.arrayOf(
692
- PropTypes.shape({
693
- // A json object key so we can create json objects out of each row
694
- key: PropTypes.string,
695
- // A human-readable title of what the column must contain
696
- title: PropTypes.string,
697
- // A human-readable description of what the column must contain
698
- description: PropTypes.string,
699
- // If true, this header must be matched to continue
700
- required: PropTypes.bool,
701
- /* A function that returns true if a cell in the column is valid, or a
702
- * string matching our built-in validators (see top of component) */
703
- validator: PropTypes.func,
704
- /* A list of guesses for the csv column header. If one of these matches
705
- * (and the column is valid), this will be automatically matched */
706
- titleGuesses: PropTypes.arrayOf(PropTypes.string),
707
- // If true, cells in this column may be empty
708
- allowEmpty: PropTypes.bool,
709
- })
710
- ),
711
- /* If true, the csv is not expected to have any header (and csv.data is
712
- * expected to be an array of row arrays)
713
- * example:
714
- * - csv.headers: null
715
- * - csv.data: [['Gabe', 'gabe@example.com'], ...]
716
- * otherwise, csv.headers is read as the list of keys to use in extracting
717
- * cell data from csv.data
718
- * example:
719
- * - csv.headers = ['Full Name', 'email']
720
- * - csv.data = [{'Full Name': 'Gabe', email: 'gabe@example.com'}, ...])
721
- */
722
- noHeader: PropTypes.bool,
723
- // The number of rows to show in the csv preview (null to show all)
724
- numRowsToPreview: PropTypes.number,
725
- // A handler to call when the "choose new file" button is clicked
726
- onReupload: PropTypes.func,
727
- // A handler to call when matching is finished
728
- onFinished: PropTypes.func,
729
- /* If defined, a download button is shown and the downloaded file has this
730
- * name */
731
- downloadFilename: PropTypes.string,
732
- };
733
-
734
- CSV.defaultProps = {
735
- headersToMatch: null,
736
- noHeader: false,
737
- numRowsToPreview: null,
738
- onReupload: null,
739
- onFinished: null,
740
- downloadFilename: null,
741
- };
742
-
743
- export default CSV;