dce-reactkit 4.0.0-beta.3 → 4.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 (38) hide show
  1. package/.vscode/settings.json +3 -1
  2. package/dist/cjs/index.js +392 -167
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/types/components/ItemPicker/index.d.ts +1 -0
  5. package/dist/cjs/types/components/Modal/ModalProps.d.ts +2 -0
  6. package/dist/cjs/types/components/SimpleDateChooser.d.ts +3 -1
  7. package/dist/cjs/types/components/SimpleTimeChooser.d.ts +20 -0
  8. package/dist/cjs/types/components/TabBox.d.ts +1 -0
  9. package/dist/cjs/types/helpers/getWordCount.d.ts +10 -0
  10. package/dist/cjs/types/index.d.ts +3 -1
  11. package/dist/cjs/types/types/ReactKitErrorCode.d.ts +3 -1
  12. package/dist/esm/index.js +392 -169
  13. package/dist/esm/index.js.map +1 -1
  14. package/dist/esm/types/components/ItemPicker/index.d.ts +1 -0
  15. package/dist/esm/types/components/Modal/ModalProps.d.ts +2 -0
  16. package/dist/esm/types/components/SimpleDateChooser.d.ts +3 -1
  17. package/dist/esm/types/components/SimpleTimeChooser.d.ts +20 -0
  18. package/dist/esm/types/components/TabBox.d.ts +1 -0
  19. package/dist/esm/types/helpers/getWordCount.d.ts +10 -0
  20. package/dist/esm/types/index.d.ts +3 -1
  21. package/dist/esm/types/types/ReactKitErrorCode.d.ts +3 -1
  22. package/dist/index.d.ts +55 -17
  23. package/package.json +2 -3
  24. package/rollup.config.js +0 -2
  25. package/src/components/IntelliTable.tsx +1 -1
  26. package/src/components/ItemPicker/NestableItemList.tsx +1 -0
  27. package/src/components/ItemPicker/index.tsx +96 -0
  28. package/src/components/LogReviewer.tsx +2 -2
  29. package/src/components/Modal/ModalProps.ts +4 -0
  30. package/src/components/Modal/index.tsx +59 -20
  31. package/src/components/SimpleDateChooser.tsx +62 -26
  32. package/src/components/SimpleTimeChooser.tsx +196 -0
  33. package/src/components/TabBox.tsx +27 -9
  34. package/src/helpers/getWordCount.ts +26 -0
  35. package/src/index.ts +4 -0
  36. package/src/types/LogSource.ts +1 -1
  37. package/src/types/ReactKitErrorCode.tsx +3 -1
  38. package/genEncodedSecret.ts +0 -84
@@ -14,6 +14,7 @@ type Props = {
14
14
  */
15
15
  onChanged: (updatedItems: PickableItem[]) => void;
16
16
  noBottomMargin?: boolean;
17
+ hideSelectAllOrNoneButtons?: boolean;
17
18
  };
18
19
  declare const ItemPicker: React.FC<Props>;
19
20
  export default ItemPicker;
@@ -38,5 +38,7 @@ type ModalProps = {
38
38
  confirmLabel?: string;
39
39
  confirmVariant?: Variant;
40
40
  onTopOfOtherModals?: boolean;
41
+ isLoading?: boolean;
42
+ isLoadingCancelable?: boolean;
41
43
  };
42
44
  export default ModalProps;
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * A very simple, lightweight date chooser
3
3
  * @author Gabe Abrams
4
+ * @author Gardenia Liu
4
5
  */
5
6
  import React from 'react';
6
7
  type Props = {
@@ -17,7 +18,8 @@ type Props = {
17
18
  */
18
19
  onChange: (month: number, day: number, year: number) => void;
19
20
  numMonthsToShow?: number;
20
- chooseFromPast?: boolean;
21
+ dontAllowPast?: boolean;
22
+ dontAllowFuture?: boolean;
21
23
  };
22
24
  declare const SimpleDateChooser: React.FC<Props>;
23
25
  export default SimpleDateChooser;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A very simple, lightweight time chooser
3
+ * @author Gardenia Liu
4
+ */
5
+ import React from 'react';
6
+ type Props = {
7
+ ariaLabel: string;
8
+ name: string;
9
+ hour: number;
10
+ minute: number;
11
+ /**
12
+ * Handler for when time changes
13
+ * @param hour new 24hr hour number
14
+ * @param minute new minute number
15
+ */
16
+ onChange: (hour: number, minute: number) => void;
17
+ intervalMin?: number;
18
+ };
19
+ declare const SimpleTimeChooser: React.FC<Props>;
20
+ export default SimpleTimeChooser;
@@ -6,6 +6,7 @@ import React from 'react';
6
6
  type Props = {
7
7
  title: React.ReactNode;
8
8
  children: React.ReactNode;
9
+ topRightChildren?: React.ReactNode;
9
10
  noBottomMargin?: boolean;
10
11
  noBottomPadding?: boolean;
11
12
  };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Get number of words in string
3
+ * @author Gardenia Liu
4
+ * @author Allison Zhang
5
+ * @author Gabe Abrams
6
+ * @param text the string to check
7
+ * @returns number of words in the string
8
+ */
9
+ declare const getWordCount: (text: string) => number;
10
+ export default getWordCount;
@@ -7,6 +7,7 @@ import RadioButton from './components/RadioButton';
7
7
  import CheckboxButton from './components/CheckboxButton';
8
8
  import ButtonInputGroup from './components/ButtonInputGroup';
9
9
  import SimpleDateChooser from './components/SimpleDateChooser';
10
+ import SimpleTimeChooser from './components/SimpleTimeChooser';
10
11
  import Drawer from './components/Drawer';
11
12
  import PopSuccessMark from './components/PopSuccessMark';
12
13
  import PopFailureMark from './components/PopFailureMark';
@@ -75,6 +76,7 @@ import mapAsync from './helpers/asyncArrayFunctions/mapAsync';
75
76
  import someAsync from './helpers/asyncArrayFunctions/someAsync';
76
77
  import capitalize from './helpers/capitalize';
77
78
  import shuffleArray from './helpers/shuffleArray';
79
+ import getWordCount from './helpers/getWordCount';
78
80
  import ParamType from './types/ParamType';
79
81
  import ModalButtonType from './types/ModalButtonType';
80
82
  import ModalSize from './types/ModalSize';
@@ -99,4 +101,4 @@ import PickableItem from './components/ItemPicker/types/PickableItem';
99
101
  import DBEntry from './components/DBEntryManagerPanel/types/DBEntry';
100
102
  import DBEntryField from './components/DBEntryManagerPanel/types/DBEntryField';
101
103
  import DBEntryFieldType from './components/DBEntryManagerPanel/types/DBEntryFieldType';
102
- export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, DBEntryManagerPanel, Tooltip, ToggleSwitch, AutoscrollToBottomContainer, MultiSwitch, Dropdown, alert, prompt, confirm, showFatalError, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_ROUTE_PATH, LOG_REVIEW_STATUS_ROUTE, DynamicWord, abbreviate, avg, ceilToNumDecimals, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, stubServerEndpoint, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, canReviewLogs, isMobileOrTablet, extractProp, compareArraysByProp, genCommaList, validateEmail, validatePhoneNumber, validateString, getLocalTimeInfo, idify, makeLinksClickable, prefixWithAOrAn, everyAsync, filterAsync, forEachAsync, mapAsync, someAsync, capitalize, shuffleArray, initClient, visitServerEndpoint, logClientEvent, addFatalErrorHandler, leaveToURL, combineClassNames, useForceRender, setClientEventMetadataPopulator, ParamType, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, LogFunction, LogTypeSpecificInfo, LogMainInfo, LogSourceSpecificInfo, LogLevel, IntelliTableColumn, DropdownItemType, PickableItem, DBEntry, DBEntryField, DBEntryFieldType, };
104
+ export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, SimpleTimeChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, DBEntryManagerPanel, Tooltip, ToggleSwitch, AutoscrollToBottomContainer, MultiSwitch, Dropdown, alert, prompt, confirm, showFatalError, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_ROUTE_PATH, LOG_REVIEW_STATUS_ROUTE, DynamicWord, abbreviate, avg, ceilToNumDecimals, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, stubServerEndpoint, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, canReviewLogs, isMobileOrTablet, extractProp, compareArraysByProp, genCommaList, validateEmail, validatePhoneNumber, validateString, getLocalTimeInfo, idify, makeLinksClickable, prefixWithAOrAn, everyAsync, filterAsync, forEachAsync, mapAsync, someAsync, capitalize, shuffleArray, getWordCount, initClient, visitServerEndpoint, logClientEvent, addFatalErrorHandler, leaveToURL, combineClassNames, useForceRender, setClientEventMetadataPopulator, ParamType, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, LogFunction, LogTypeSpecificInfo, LogMainInfo, LogSourceSpecificInfo, LogLevel, IntelliTableColumn, DropdownItemType, PickableItem, DBEntry, DBEntryField, DBEntryFieldType, };
@@ -6,6 +6,8 @@ declare enum ReactKitErrorCode {
6
6
  NoResponse = "DRK1",
7
7
  NoCode = "DRK2",
8
8
  SessionExpired = "DRK3",
9
- NoCACCLSendRequestFunction = "DRK7"
9
+ NoCACCLSendRequestFunction = "DRK7",
10
+ SimpleDateChooserInvalidDateRange = "DRK35",
11
+ SimpleDateChooserInvalidNumMonths = "DRK36"
10
12
  }
11
13
  export default ReactKitErrorCode;
package/dist/index.d.ts CHANGED
@@ -23,7 +23,7 @@ declare enum Variant {
23
23
  * @author Gabe Abrams
24
24
  */
25
25
 
26
- type Props$l = {
26
+ type Props$m = {
27
27
  children: React$1.ReactNode;
28
28
  };
29
29
  /**
@@ -106,7 +106,7 @@ declare const showFatalError: (error: any, errorTitle?: string) => Promise<void>
106
106
  * @author Gabe Abrams
107
107
  */
108
108
  declare const addFatalErrorHandler: (handler: () => void) => void;
109
- declare const AppWrapper: React$1.FC<Props$l>;
109
+ declare const AppWrapper: React$1.FC<Props$m>;
110
110
 
111
111
  /**
112
112
  * Loading spinner/indicator
@@ -120,14 +120,14 @@ declare const LoadingSpinner: () => React$1.JSX.Element;
120
120
  * @author Gabe Abrams
121
121
  */
122
122
 
123
- type Props$k = {
123
+ type Props$l = {
124
124
  error: any;
125
125
  title?: string;
126
126
  onClose?: () => void;
127
127
  variant?: Variant;
128
128
  icon?: IconProp;
129
129
  };
130
- declare const ErrorBox: React$1.FC<Props$k>;
130
+ declare const ErrorBox: React$1.FC<Props$l>;
131
131
 
132
132
  /**
133
133
  * Types of buttons in the modal
@@ -209,6 +209,8 @@ type ModalProps = {
209
209
  confirmLabel?: string;
210
210
  confirmVariant?: Variant;
211
211
  onTopOfOtherModals?: boolean;
212
+ isLoading?: boolean;
213
+ isLoadingCancelable?: boolean;
212
214
  };
213
215
 
214
216
  /**
@@ -224,20 +226,21 @@ declare const Modal: React$1.FC<ModalProps>;
224
226
  * @author Gabe Abrams
225
227
  */
226
228
 
227
- type Props$j = {
229
+ type Props$k = {
228
230
  title: React$1.ReactNode;
229
231
  children: React$1.ReactNode;
232
+ topRightChildren?: React$1.ReactNode;
230
233
  noBottomMargin?: boolean;
231
234
  noBottomPadding?: boolean;
232
235
  };
233
- declare const TabBox: React$1.FC<Props$j>;
236
+ declare const TabBox: React$1.FC<Props$k>;
234
237
 
235
238
  /**
236
239
  * A radio selection button
237
240
  * @author Gabe Abrams
238
241
  */
239
242
 
240
- type Props$i = {
243
+ type Props$j = {
241
244
  text: React$1.ReactNode;
242
245
  onSelected: () => void;
243
246
  ariaLabel: string;
@@ -251,14 +254,14 @@ type Props$i = {
251
254
  small?: boolean;
252
255
  useComplexFormatting?: boolean;
253
256
  };
254
- declare const RadioButton: React$1.FC<Props$i>;
257
+ declare const RadioButton: React$1.FC<Props$j>;
255
258
 
256
259
  /**
257
260
  * A checkbox button
258
261
  * @author Gabe Abrams
259
262
  */
260
263
 
261
- type Props$h = {
264
+ type Props$i = {
262
265
  text: React$1.ReactNode;
263
266
  onChanged: (checked: boolean) => void;
264
267
  ariaLabel: string;
@@ -273,14 +276,14 @@ type Props$h = {
273
276
  dashed?: boolean;
274
277
  useComplexFormatting?: boolean;
275
278
  };
276
- declare const CheckboxButton: React$1.FC<Props$h>;
279
+ declare const CheckboxButton: React$1.FC<Props$i>;
277
280
 
278
281
  /**
279
282
  * Input group with a title and space for buttons
280
283
  * @author Gabe Abrams
281
284
  */
282
285
 
283
- type Props$g = {
286
+ type Props$h = {
284
287
  label: React$1.ReactNode;
285
288
  minLabelWidth?: string;
286
289
  children: React$1.ReactNode;
@@ -289,14 +292,15 @@ type Props$g = {
289
292
  isAdminFeature?: boolean;
290
293
  noMarginOnBottom?: boolean;
291
294
  };
292
- declare const ButtonInputGroup: React$1.FC<Props$g>;
295
+ declare const ButtonInputGroup: React$1.FC<Props$h>;
293
296
 
294
297
  /**
295
298
  * A very simple, lightweight date chooser
296
299
  * @author Gabe Abrams
300
+ * @author Gardenia Liu
297
301
  */
298
302
 
299
- type Props$f = {
303
+ type Props$g = {
300
304
  ariaLabel: string;
301
305
  name: string;
302
306
  month: number;
@@ -310,9 +314,30 @@ type Props$f = {
310
314
  */
311
315
  onChange: (month: number, day: number, year: number) => void;
312
316
  numMonthsToShow?: number;
313
- chooseFromPast?: boolean;
317
+ dontAllowPast?: boolean;
318
+ dontAllowFuture?: boolean;
314
319
  };
315
- declare const SimpleDateChooser: React$1.FC<Props$f>;
320
+ declare const SimpleDateChooser: React$1.FC<Props$g>;
321
+
322
+ /**
323
+ * A very simple, lightweight time chooser
324
+ * @author Gardenia Liu
325
+ */
326
+
327
+ type Props$f = {
328
+ ariaLabel: string;
329
+ name: string;
330
+ hour: number;
331
+ minute: number;
332
+ /**
333
+ * Handler for when time changes
334
+ * @param hour new 24hr hour number
335
+ * @param minute new minute number
336
+ */
337
+ onChange: (hour: number, minute: number) => void;
338
+ intervalMin?: number;
339
+ };
340
+ declare const SimpleTimeChooser: React$1.FC<Props$f>;
316
341
 
317
342
  /**
318
343
  * Drawer container
@@ -412,6 +437,7 @@ type Props$9 = {
412
437
  */
413
438
  onChanged: (updatedItems: PickableItem[]) => void;
414
439
  noBottomMargin?: boolean;
440
+ hideSelectAllOrNoneButtons?: boolean;
415
441
  };
416
442
  declare const ItemPicker: React$1.FC<Props$9>;
417
443
 
@@ -1498,6 +1524,16 @@ declare const capitalize: (str: string) => string;
1498
1524
  */
1499
1525
  declare const shuffleArray: <T>(arr: T[]) => T[];
1500
1526
 
1527
+ /**
1528
+ * Get number of words in string
1529
+ * @author Gardenia Liu
1530
+ * @author Allison Zhang
1531
+ * @author Gabe Abrams
1532
+ * @param text the string to check
1533
+ * @returns number of words in the string
1534
+ */
1535
+ declare const getWordCount: (text: string) => number;
1536
+
1501
1537
  /**
1502
1538
  * Days of the week
1503
1539
  * @author Gabe Abrams
@@ -1536,7 +1572,9 @@ declare enum ReactKitErrorCode {
1536
1572
  NoResponse = "DRK1",
1537
1573
  NoCode = "DRK2",
1538
1574
  SessionExpired = "DRK3",
1539
- NoCACCLSendRequestFunction = "DRK7"
1575
+ NoCACCLSendRequestFunction = "DRK7",
1576
+ SimpleDateChooserInvalidDateRange = "DRK35",
1577
+ SimpleDateChooserInvalidNumMonths = "DRK36"
1540
1578
  }
1541
1579
 
1542
- export { AppWrapper, AutoscrollToBottomContainer, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntry, DBEntryField, DBEntryFieldType, DBEntryManagerPanel, DayOfWeek, Drawer, Dropdown, DropdownItemType, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, IntelliTableColumn, ItemPicker, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_REVIEW_STATUS_ROUTE, LOG_ROUTE_PATH, LoadingSpinner, Log, LogAction, LogBuiltInMetadata, LogFunction, LogLevel, LogMainInfo, LogMetadataType, LogReviewer, LogSource, LogSourceSpecificInfo, LogType, LogTypeSpecificInfo, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, MultiSwitch, ParamType, PickableItem, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, ToggleSwitch, Tooltip, Variant, abbreviate, addFatalErrorHandler, alert, avg, canReviewLogs, capitalize, ceilToNumDecimals, combineClassNames, compareArraysByProp, confirm, everyAsync, extractProp, filterAsync, floorToNumDecimals, forEachAsync, forceNumIntoBounds, genCSV, genCommaList, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, idify, initClient, isMobileOrTablet, leaveToURL, logClientEvent, makeLinksClickable, mapAsync, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, prompt, roundToNumDecimals, setClientEventMetadataPopulator, showFatalError, shuffleArray, someAsync, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, useForceRender, validateEmail, validatePhoneNumber, validateString, visitServerEndpoint, waitMs };
1580
+ export { AppWrapper, AutoscrollToBottomContainer, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntry, DBEntryField, DBEntryFieldType, DBEntryManagerPanel, DayOfWeek, Drawer, Dropdown, DropdownItemType, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, IntelliTableColumn, ItemPicker, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_REVIEW_STATUS_ROUTE, LOG_ROUTE_PATH, LoadingSpinner, Log, LogAction, LogBuiltInMetadata, LogFunction, LogLevel, LogMainInfo, LogMetadataType, LogReviewer, LogSource, LogSourceSpecificInfo, LogType, LogTypeSpecificInfo, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, MultiSwitch, ParamType, PickableItem, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, SimpleTimeChooser, TabBox, ToggleSwitch, Tooltip, Variant, abbreviate, addFatalErrorHandler, alert, avg, canReviewLogs, capitalize, ceilToNumDecimals, combineClassNames, compareArraysByProp, confirm, everyAsync, extractProp, filterAsync, floorToNumDecimals, forEachAsync, forceNumIntoBounds, genCSV, genCommaList, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, getWordCount, idify, initClient, isMobileOrTablet, leaveToURL, logClientEvent, makeLinksClickable, mapAsync, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, prompt, roundToNumDecimals, setClientEventMetadataPopulator, showFatalError, shuffleArray, someAsync, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, useForceRender, validateEmail, validatePhoneNumber, validateString, visitServerEndpoint, waitMs };
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "dce-reactkit",
3
- "version": "4.0.0-beta.3",
3
+ "version": "4.0.1",
4
4
  "description": "Shared components for Harvard DCE apps",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "scripts": {
9
9
  "build": "rm -rf dist && rollup -c",
10
- "test": "jest --runInBand",
11
- "gen-reactkit-cross-server-secret": "npx tsx genEncodedSecret.ts"
10
+ "test": "jest --runInBand"
12
11
  },
13
12
  "jest": {
14
13
  "preset": "ts-jest",
package/rollup.config.js CHANGED
@@ -27,7 +27,6 @@ export default [
27
27
  typescript({ tsconfig: "./tsconfig.json" }),
28
28
  sourcemaps(),
29
29
  ],
30
- inlineDynamicImports: true,
31
30
  external: [
32
31
  ...Object.keys(packageJson.dependencies || {}),
33
32
  ...Object.keys(packageJson.peerDependencies || {}),
@@ -37,7 +36,6 @@ export default [
37
36
  input: "dist/esm/types/index.d.ts",
38
37
  output: [{ file: "dist/index.d.ts", format: "esm" }],
39
38
  plugins: [dts()],
40
- inlineDynamicImports: true,
41
39
  external: [
42
40
  ...Object.keys(packageJson.dependencies || {}),
43
41
  ...Object.keys(packageJson.peerDependencies || {}),
@@ -315,7 +315,7 @@ const IntelliTable: React.FC<Props> = (props) => {
315
315
  checked={columnVisibilityMap[column.param]}
316
316
  ariaLabel={`show "${column.title}" column in the ${title} table`}
317
317
  checkedVariant={Variant.Light}
318
- uncheckedVariant={Variant.Secondary}
318
+ uncheckedVariant={Variant.Light}
319
319
  />
320
320
  );
321
321
  })
@@ -272,6 +272,7 @@ const NestableItemList: React.FC<Props> = (props) => {
272
272
  }}
273
273
  ariaLabel={`Select ${item.name}`}
274
274
  checkedVariant={Variant.Light}
275
+ uncheckedVariant={Variant.Light}
275
276
  />
276
277
 
277
278
  {/* Children */}
@@ -33,6 +33,8 @@ type Props = {
33
33
  onChanged: (updatedItems: PickableItem[]) => void,
34
34
  // If true, don't add margin to bottom of item picker
35
35
  noBottomMargin?: boolean,
36
+ // If true, hide select all or none buttons
37
+ hideSelectAllOrNoneButtons?: boolean,
36
38
  };
37
39
 
38
40
  /*------------------------------------------------------------------------*/
@@ -52,12 +54,61 @@ const ItemPicker: React.FC<Props> = (props) => {
52
54
  items,
53
55
  onChanged,
54
56
  noBottomMargin,
57
+ hideSelectAllOrNoneButtons,
55
58
  } = props;
56
59
 
57
60
  /*------------------------------------------------------------------------*/
58
61
  /* ------------------------- Component Functions ------------------------ */
59
62
  /*------------------------------------------------------------------------*/
60
63
 
64
+ /**
65
+ * Updates the checked state of an item (including all children)
66
+ * @author Yuen Ler Chow
67
+ * @param item the item to update
68
+ * @param checked the new checked state
69
+ * @returns the updated item
70
+ */
71
+ const updateItemChecked = (
72
+ item: PickableItem,
73
+ checked: boolean,
74
+ ): PickableItem => {
75
+ if (item.isGroup) {
76
+ return {
77
+ ...item,
78
+ children: item.children.map((child) => {
79
+ return updateItemChecked(child, checked);
80
+ }),
81
+ };
82
+ }
83
+ return { ...item, checked };
84
+ };
85
+
86
+ /**
87
+ * Selects all items in the list
88
+ * @author Yuen Ler Chow
89
+ */
90
+ const handleSelectAll = () => {
91
+ const updatedItems = items.map(
92
+ (item) => {
93
+ return updateItemChecked(item, true);
94
+ },
95
+ );
96
+ onChanged(updatedItems);
97
+ };
98
+
99
+ /**
100
+ * Deselects all items in the list
101
+ * @author Yuen Ler Chow
102
+ */
103
+ const handleDeselectAll = () => {
104
+ const updatedItems = items.map(
105
+ (item) => {
106
+ return updateItemChecked(item, false);
107
+ },
108
+ );
109
+ onChanged(updatedItems);
110
+ };
111
+
61
112
  /*------------------------------------------------------------------------*/
62
113
  /* ------------------------------- Render ------------------------------- */
63
114
  /*------------------------------------------------------------------------*/
@@ -66,10 +117,55 @@ const ItemPicker: React.FC<Props> = (props) => {
66
117
  /* --------------- Main UI -------------- */
67
118
  /*----------------------------------------*/
68
119
 
120
+ // Select all/none
121
+ const selectAllOrNone = (
122
+ !hideSelectAllOrNoneButtons
123
+ ? (
124
+ <div
125
+ className="d-flex h-100 align-items-end flex-row"
126
+ >
127
+ <div className="d-flex justify-content-end">
128
+ {/* Label */}
129
+ <div
130
+ className="me-2"
131
+ style={{ fontSize: '1.2rem' }}
132
+ >
133
+ Select
134
+ </div>
135
+ {/* Buttons */}
136
+ <div className="btn-group" role="group">
137
+ {/* All */}
138
+ <button
139
+ type="button"
140
+ style={{ borderRight: '0.1rem solid white' }}
141
+ aria-label="Select all contexts"
142
+ className="btn btn-secondary py-0"
143
+ onClick={handleSelectAll}
144
+ >
145
+ All
146
+ </button>
147
+ {/* None */}
148
+ <button
149
+ type="button"
150
+ aria-label="Deselect all contexts"
151
+ className="btn btn-secondary py-0"
152
+ onClick={handleDeselectAll}
153
+ >
154
+ None
155
+ </button>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ )
160
+ : undefined
161
+ );
162
+
163
+ // Main UI
69
164
  return (
70
165
  <TabBox
71
166
  title={title}
72
167
  noBottomMargin={noBottomMargin}
168
+ topRightChildren={selectAllOrNone}
73
169
  >
74
170
  <div style={{ overflowX: 'auto' }}>
75
171
  <NestableItemList
@@ -1182,7 +1182,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1182
1182
  year={dateFilterState.startDate.year}
1183
1183
  month={dateFilterState.startDate.month}
1184
1184
  day={dateFilterState.startDate.day}
1185
- chooseFromPast
1185
+ dontAllowFuture
1186
1186
  numMonthsToShow={36}
1187
1187
  onChange={(month, day, year) => {
1188
1188
  dateFilterState.startDate = { month, day, year };
@@ -1198,7 +1198,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1198
1198
  year={dateFilterState.endDate.year}
1199
1199
  month={dateFilterState.endDate.month}
1200
1200
  day={dateFilterState.endDate.day}
1201
- chooseFromPast
1201
+ dontAllowFuture
1202
1202
  numMonthsToShow={12}
1203
1203
  onChange={(month, day, year) => {
1204
1204
  if (
@@ -69,6 +69,10 @@ type ModalProps = {
69
69
  confirmVariant?: Variant,
70
70
  // True if modal should be on top of other modals
71
71
  onTopOfOtherModals?: boolean,
72
+ // If true, the modal is loading
73
+ isLoading?: boolean,
74
+ // If true, the loading is cancelable
75
+ isLoadingCancelable?: boolean,
72
76
  };
73
77
 
74
78
  export default ModalProps;
@@ -14,6 +14,9 @@ import React, { useState, useEffect, useRef } from 'react';
14
14
  import ReactDOM from 'react-dom';
15
15
 
16
16
  // Import other components
17
+ import LoadingSpinner from '../LoadingSpinner';
18
+
19
+ // Import helpers
17
20
  import waitMs from '../../helpers/waitMs';
18
21
 
19
22
  // Import types
@@ -41,6 +44,7 @@ const BASE_Z_INDEX_ON_TOP = 2000000000;
41
44
 
42
45
  // Constants
43
46
  const MS_TO_ANIMATE = 200; // Animation duration
47
+ const MS_TO_WAIT_BEFORE_SHOWING_LOADING_INDICATOR = 1000;
44
48
 
45
49
  // Modal type to list of buttons
46
50
  const modalTypeToModalButtonTypes: {
@@ -256,6 +260,8 @@ const Modal: React.FC<ModalProps> = (props) => {
256
260
  dontAllowBackdropExit,
257
261
  dontShowXButton,
258
262
  onTopOfOtherModals,
263
+ isLoading,
264
+ isLoadingCancelable,
259
265
  } = props;
260
266
 
261
267
  // Determine if no header either
@@ -266,6 +272,7 @@ const Modal: React.FC<ModalProps> = (props) => {
266
272
  // True if animation is in use
267
273
  const [animatingIn, setAnimatingIn] = useState(true);
268
274
  const [animatingPop, setAnimatingPop] = useState(false);
275
+ const [showModal, setShowModal] = useState(false);
269
276
 
270
277
  /* -------------- Refs -------------- */
271
278
 
@@ -298,6 +305,15 @@ const Modal: React.FC<ModalProps> = (props) => {
298
305
  if (mounted.current) {
299
306
  setAnimatingIn(false);
300
307
  }
308
+ if (isLoading) {
309
+ // wait before showing modal
310
+ await waitMs(MS_TO_WAIT_BEFORE_SHOWING_LOADING_INDICATOR);
311
+ if (mounted.current) {
312
+ setShowModal(true);
313
+ }
314
+ } else {
315
+ setShowModal(true);
316
+ }
301
317
  })();
302
318
 
303
319
  return () => {
@@ -400,6 +416,17 @@ const Modal: React.FC<ModalProps> = (props) => {
400
416
  animationClass = 'Modal-animating-pop';
401
417
  }
402
418
 
419
+ // default to show close button when not loading and not show close button when loading
420
+ // unless downShowXButton or isLoadingCancelable
421
+ const showCloseButton = (
422
+ // The modal must have an onClose function
423
+ onClose
424
+ // ...and the close button is allowed to be visible
425
+ && !dontShowXButton
426
+ // ...and should not be shown if the modal is loading and not cancelable
427
+ && !(isLoading && !isLoadingCancelable)
428
+ );
429
+
403
430
  // Render the modal
404
431
  const contentToRender = (
405
432
  <div
@@ -436,6 +463,8 @@ const Modal: React.FC<ModalProps> = (props) => {
436
463
  handleClose(ModalButtonType.Cancel);
437
464
  }}
438
465
  />
466
+
467
+ {showModal && (
439
468
  <div
440
469
  className={`modal-dialog modal-${size} ${animationClass} modal-dialog-scrollable modal-dialog-centered`}
441
470
  style={{
@@ -498,7 +527,7 @@ const Modal: React.FC<ModalProps> = (props) => {
498
527
  {title}
499
528
  </h5>
500
529
 
501
- {(onClose && !dontShowXButton) && (
530
+ {showCloseButton && (
502
531
  <button
503
532
  type="button"
504
533
  className="Modal-x-button btn-close"
@@ -518,25 +547,34 @@ const Modal: React.FC<ModalProps> = (props) => {
518
547
  )}
519
548
  </div>
520
549
  )}
521
- {children && (
522
- <div
523
- className="modal-body"
524
- style={{
525
- color: (
526
- isDarkModeOn()
527
- ? 'white'
528
- : undefined
529
- ),
530
- backgroundColor: (
531
- isDarkModeOn()
532
- ? '#444'
533
- : undefined
534
- ),
535
- }}
536
- >
537
- {children}
538
- </div>
539
- )}
550
+
551
+ <div
552
+ className="modal-body"
553
+ style={{
554
+ color: (
555
+ isDarkModeOn()
556
+ ? 'white'
557
+ : undefined
558
+ ),
559
+ backgroundColor: (
560
+ isDarkModeOn()
561
+ ? '#444'
562
+ : undefined
563
+ ),
564
+ }}
565
+ >
566
+ {
567
+ isLoading
568
+ ? (
569
+ <>
570
+ <LoadingSpinner />
571
+ <span className="sr-only">Content loading</span>
572
+ </>
573
+ )
574
+ : children
575
+ }
576
+ </div>
577
+
540
578
  {footer && (
541
579
  <div
542
580
  className="modal-footer pt-1 pb-1"
@@ -563,6 +601,7 @@ const Modal: React.FC<ModalProps> = (props) => {
563
601
  )}
564
602
  </div>
565
603
  </div>
604
+ )}
566
605
  </div>
567
606
  );
568
607