dce-reactkit 3.2.8 → 3.3.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.
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Type of CACCL's send request function
3
+ * @author Gabe Abrams
4
+ */
5
+ declare type SendRequestFunction = (opts: {
6
+ path: string;
7
+ method: ('GET' | 'POST' | 'DELETE' | 'PUT');
8
+ params?: {
9
+ [x: string]: any;
10
+ } | undefined;
11
+ headers?: {
12
+ [x: string]: any;
13
+ } | undefined;
14
+ numRetries?: number | undefined;
15
+ }) => Promise<{
16
+ body: any;
17
+ status: number;
18
+ headers: {
19
+ [x: string]: any;
20
+ };
21
+ }>;
22
+ /**
23
+ * Get the send request function
24
+ * @author Gabe Abrams
25
+ * @returns sendRequest function
26
+ */
27
+ export declare const getSendRequest: () => SendRequestFunction;
28
+ declare let dark: boolean | undefined;
29
+ /**
30
+ * Get the current dark/light theme
31
+ * @author Gabe Abrams
32
+ * @returns true if the app has a dark theme
33
+ */
34
+ export declare const darkModeIsOn: () => boolean;
35
+ declare let sessionExpiredMessage: string | undefined;
36
+ /**
37
+ * Get the custom session expired message
38
+ * @author Gabe Abrams
39
+ * @returns session expired message
40
+ */
41
+ export declare const getSessionExpiredMessage: () => string;
42
+ /**
43
+ * Initialize the client-side version of reactkit
44
+ * @author Gabe Abrams
45
+ * @param opts object containing all arguments
46
+ * @param opts.sendRequest caccl send request functions
47
+ * @param [opts.dark] if true, the app is a dark-themed app
48
+ * @param [opts.sessionExpiredMessage] a custom session expired message
49
+ */
50
+ declare const initClient: (opts: {
51
+ sendRequest: SendRequestFunction;
52
+ dark?: boolean;
53
+ sessionExpiredMessage?: string;
54
+ }) => void;
55
+ export default initClient;
@@ -7,34 +7,7 @@ import React from 'react';
7
7
  import Variant from '../types/Variant';
8
8
  declare type Props = {
9
9
  children: React.ReactNode;
10
- sendRequest: SendRequestFunction;
11
- dark?: boolean;
12
- sessionExpiredMessage?: string;
13
10
  };
14
- declare type SendRequestFunction = (opts: {
15
- path: string;
16
- method: ('GET' | 'POST' | 'DELETE' | 'PUT');
17
- params?: {
18
- [x: string]: any;
19
- } | undefined;
20
- headers?: {
21
- [x: string]: any;
22
- } | undefined;
23
- numRetries?: number | undefined;
24
- }) => Promise<{
25
- body: any;
26
- status: number;
27
- headers: {
28
- [x: string]: any;
29
- };
30
- }>;
31
- /**
32
- * Send a request using caccl's send request feature
33
- * @author Gabe Abrams
34
- * @param opts send request options
35
- * @returns send request response
36
- */
37
- export declare const cacclSendRequest: SendRequestFunction;
38
11
  /**
39
12
  * Show an alert modal with an "Okay" button
40
13
  * @author Gabe Abrams
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Compare two arrays of objects by only comparing the values in a specific
3
+ * property (e.g. compare user arrays by comparing their user.id values)
4
+ * @author Gabe Abrams
5
+ * @param a the first array
6
+ * @param b the second array
7
+ * @param prop the property to compare with
8
+ * @returns true if the arrays contain the same objects as determined by
9
+ * the values associated with each object's prop
10
+ */
11
+ declare const compareArraysByProp: (a: any[], b: any[], prop: string) => boolean;
12
+ export default compareArraysByProp;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * For every element in an array, extract the value of a prop
3
+ * (e.g. for all user objects, extract their ages and put that into a new
4
+ * ages array)
5
+ * @author Gabe Abrams
6
+ * @param arr the array of objects to operate on
7
+ * @param prop the property to extract from each object
8
+ * @returns new array containing the corresponding values, in order, of each
9
+ * object in the original array
10
+ */
11
+ declare const extractProp: (arr: any[], prop: string) => any[];
12
+ export default extractProp;
@@ -21,6 +21,7 @@ import MINUTE_IN_MS from './constants/MINUTE_IN_MS';
21
21
  import HOUR_IN_MS from './constants/HOUR_IN_MS';
22
22
  import DAY_IN_MS from './constants/DAY_IN_MS';
23
23
  import DynamicWord from './dynamicConstants/DynamicWord';
24
+ import initClient from './client/initClient';
24
25
  import abbreviate from './helpers/abbreviate';
25
26
  import avg from './helpers/avg';
26
27
  import ceilToNumDecimals from './helpers/ceilToNumDecimals';
@@ -51,6 +52,8 @@ import getMonthName from './helpers/getMonthName';
51
52
  import genCSV from './helpers/genCSV';
52
53
  import canReviewLogs from './helpers/canReviewLogs';
53
54
  import isMobileOrTablet from './helpers/isMobileOrTablet';
55
+ import extractProp from './helpers/extractProp';
56
+ import compareArraysByProp from './helpers/compareArraysByProp';
54
57
  import ModalButtonType from './types/ModalButtonType';
55
58
  import ModalSize from './types/ModalSize';
56
59
  import ModalType from './types/ModalType';
@@ -66,4 +69,4 @@ import LogBuiltInMetadata from './types/LogBuiltInMetadata';
66
69
  import LogMetadataType from './types/LogMetadataType';
67
70
  import IntelliTableColumn from './types/IntelliTableColumn';
68
71
  import PickableItem from './components/ItemPicker/types/PickableItem';
69
- export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, alert, confirm, showFatalError, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, DynamicWord, abbreviate, avg, ceilToNumDecimals, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, stubServerEndpoint, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, canReviewLogs, isMobileOrTablet, visitServerEndpoint, logClientEvent, initServer, genRouteHandler, handleError, handleSuccess, initLogCollection, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, IntelliTableColumn, PickableItem, ParamType, };
72
+ export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, alert, confirm, showFatalError, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, 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, initClient, visitServerEndpoint, logClientEvent, initServer, genRouteHandler, handleError, handleSuccess, initLogCollection, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, IntelliTableColumn, PickableItem, ParamType, };
@@ -13,6 +13,8 @@ declare enum ReactKitErrorCode {
13
13
  NoCACCLGetLaunchInfoFunction = "DRK8",
14
14
  NotTTM = "DRK9",
15
15
  NotAdmin = "DRK10",
16
- NotAllowedToReviewLogs = "DRK11"
16
+ NotAllowedToReviewLogs = "DRK11",
17
+ ThemeCheckedBeforeReactKitReady = "DRK12",
18
+ SessionExpiredMessageGottenBeforeReactKitReady = "DRK13"
17
19
  }
18
20
  export default ReactKitErrorCode;
package/dist/esm/index.js CHANGED
@@ -28,7 +28,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
28
28
  });
29
29
  }
30
30
 
31
- // Highest error code = DRK11
31
+ // Highest error code = DRK13
32
32
  /**
33
33
  * List of error codes built into the react kit
34
34
  * @author Gabe Abrams
@@ -46,6 +46,8 @@ var ReactKitErrorCode;
46
46
  ReactKitErrorCode["NotTTM"] = "DRK9";
47
47
  ReactKitErrorCode["NotAdmin"] = "DRK10";
48
48
  ReactKitErrorCode["NotAllowedToReviewLogs"] = "DRK11";
49
+ ReactKitErrorCode["ThemeCheckedBeforeReactKitReady"] = "DRK12";
50
+ ReactKitErrorCode["SessionExpiredMessageGottenBeforeReactKitReady"] = "DRK13";
49
51
  })(ReactKitErrorCode || (ReactKitErrorCode = {}));
50
52
  var ReactKitErrorCode$1 = ReactKitErrorCode;
51
53
 
@@ -141,6 +143,24 @@ var ModalType;
141
143
  })(ModalType || (ModalType = {}));
142
144
  var ModalType$1 = ModalType;
143
145
 
146
+ /**
147
+ * Built-in metadata for logs
148
+ * @author Gabe Abrams
149
+ */
150
+ const LogBuiltInMetadata = {
151
+ // Contexts
152
+ Context: {
153
+ Uncategorized: 'Uncategorized',
154
+ ServerRenderedErrorPage: 'ServerRenderedErrorPage',
155
+ ServerEndpointError: 'ServerEndpointError',
156
+ ClientFatalError: 'ClientFatalError',
157
+ },
158
+ // Targets
159
+ Target: {
160
+ NoTarget: 'NoTarget',
161
+ },
162
+ };
163
+
144
164
  /**
145
165
  * Wait for a certain number of ms
146
166
  * @author Gabe Abrams
@@ -504,24 +524,6 @@ const ROUTE_PATH_PREFIX = '/dce-reactkit';
504
524
  */
505
525
  const LOG_ROUTE_PATH = `${ROUTE_PATH_PREFIX}/log`;
506
526
 
507
- /**
508
- * Built-in metadata for logs
509
- * @author Gabe Abrams
510
- */
511
- const LogBuiltInMetadata = {
512
- // Contexts
513
- Context: {
514
- Uncategorized: 'Uncategorized',
515
- ServerRenderedErrorPage: 'ServerRenderedErrorPage',
516
- ServerEndpointError: 'ServerEndpointError',
517
- ClientFatalError: 'ClientFatalError',
518
- },
519
- // Targets
520
- Target: {
521
- NoTarget: 'NoTarget',
522
- },
523
- };
524
-
525
527
  /**
526
528
  * Allowed log levels
527
529
  * @author Gabe Abrams
@@ -534,6 +536,83 @@ var LogLevel;
534
536
  })(LogLevel || (LogLevel = {}));
535
537
  var LogLevel$1 = LogLevel;
536
538
 
539
+ // Import other components
540
+ /*----------------------------------------*/
541
+ /* ---- Static Variables and Getters ---- */
542
+ /*----------------------------------------*/
543
+ /* ----------- Initialized ---------- */
544
+ let initialized = false;
545
+ /* ---------- Send Request ---------- */
546
+ let storedSendRequest;
547
+ /**
548
+ * Get the send request function
549
+ * @author Gabe Abrams
550
+ * @returns sendRequest function
551
+ */
552
+ const getSendRequest = () => {
553
+ // Error if no send request function
554
+ if (!initialized) {
555
+ showFatalError(new ErrorWithCode('Could not send a request because the request needed to be sent before dce-reactkit was properly initialized. Perhaps dce-reactkit was not initialized with initClient.', ReactKitErrorCode$1.NoCACCLSendRequestFunction));
556
+ // Return dummy function that never resolves
557
+ return (() => {
558
+ return new Promise(() => { });
559
+ });
560
+ }
561
+ // Return
562
+ return storedSendRequest;
563
+ };
564
+ /* ------------ Dark Mode ----------- */
565
+ let dark;
566
+ /**
567
+ * Get the current dark/light theme
568
+ * @author Gabe Abrams
569
+ * @returns true if the app has a dark theme
570
+ */
571
+ const darkModeIsOn = () => {
572
+ // Error if not set up yet
573
+ if (!initialized) {
574
+ showFatalError(new ErrorWithCode('Could not check theme color because dce-reactkit was properly initialized. Perhaps dce-reactkit was not initialized with initClient.', ReactKitErrorCode$1.ThemeCheckedBeforeReactKitReady));
575
+ return false;
576
+ }
577
+ // Return
578
+ return !!dark;
579
+ };
580
+ /* ----- Session Expired Message ---- */
581
+ let sessionExpiredMessage;
582
+ /**
583
+ * Get the custom session expired message
584
+ * @author Gabe Abrams
585
+ * @returns session expired message
586
+ */
587
+ const getSessionExpiredMessage = () => {
588
+ // Error if not set up yet
589
+ if (!initialized) {
590
+ showFatalError(new ErrorWithCode('Could not get the session expired message because dce-reactkit was properly initialized. Perhaps dce-reactkit was not initialized with initClient.', ReactKitErrorCode$1.SessionExpiredMessageGottenBeforeReactKitReady));
591
+ return '';
592
+ }
593
+ // Return
594
+ return (sessionExpiredMessage !== null && sessionExpiredMessage !== void 0 ? sessionExpiredMessage : 'Your session has expired. Please go back to Canvas and start over.');
595
+ };
596
+ /*----------------------------------------*/
597
+ /* ---------------- Init ---------------- */
598
+ /*----------------------------------------*/
599
+ /**
600
+ * Initialize the client-side version of reactkit
601
+ * @author Gabe Abrams
602
+ * @param opts object containing all arguments
603
+ * @param opts.sendRequest caccl send request functions
604
+ * @param [opts.dark] if true, the app is a dark-themed app
605
+ * @param [opts.sessionExpiredMessage] a custom session expired message
606
+ */
607
+ const initClient = (opts) => {
608
+ // Store values
609
+ storedSendRequest = opts.sendRequest;
610
+ dark = !!opts.dark;
611
+ sessionExpiredMessage = opts.sessionExpiredMessage;
612
+ // Mark as initialized
613
+ initialized = true;
614
+ };
615
+
537
616
  // Keep track of whether or not session expiry has already been handled
538
617
  let sessionAlreadyExpired = false;
539
618
  /*------------------------------------------------------------------------*/
@@ -605,7 +684,8 @@ const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function
605
684
  throw new ErrorWithCode(stubResponse.errorMessage, stubResponse.errorCode);
606
685
  }
607
686
  // Send the request
608
- const response = yield cacclSendRequest({
687
+ const sendRequest = getSendRequest();
688
+ const response = yield sendRequest({
609
689
  path: opts.path,
610
690
  method: (_c = opts.method) !== null && _c !== void 0 ? _c : 'GET',
611
691
  params: opts.params,
@@ -692,24 +772,6 @@ const logClientEvent = (opts) => __awaiter(void 0, void 0, void 0, function* ()
692
772
  /* Static Helpers */
693
773
  /*------------------------------------------------------------------------*/
694
774
  /*----------------------------------------*/
695
- /* Send Request */
696
- /*----------------------------------------*/
697
- // Store copy of caccl send request
698
- let _cacclSendRequest;
699
- /**
700
- * Send a request using caccl's send request feature
701
- * @author Gabe Abrams
702
- * @param opts send request options
703
- * @returns send request response
704
- */
705
- const cacclSendRequest = (opts) => __awaiter(void 0, void 0, void 0, function* () {
706
- // Make sure send request has been passed in
707
- if (!_cacclSendRequest) {
708
- throw new ErrorWithCode(`\nThe request could not be sent because the AppWrapper component does not have a copy of sendRequest from CACCL.\nIf you are currently writing tests for your app, this means you did not properly stub the server response.\nMethod: ${opts.method}\nPath: ${opts.path}\n\n`, ReactKitErrorCode$1.NoCACCLSendRequestFunction);
709
- }
710
- return _cacclSendRequest(opts);
711
- });
712
- /*----------------------------------------*/
713
775
  /* Alert */
714
776
  /*----------------------------------------*/
715
777
  // Stored copies of setters
@@ -833,9 +895,7 @@ const AppWrapper = (props) => {
833
895
  /* Setup */
834
896
  /*------------------------------------------------------------------------*/
835
897
  /* -------------- Props ------------- */
836
- const { children, sendRequest, dark, sessionExpiredMessage = 'Your session has expired. Please go back to Canvas and start over.', } = props;
837
- // Store copy of send request
838
- _cacclSendRequest = sendRequest;
898
+ const { children, } = props;
839
899
  /* -------------- State ------------- */
840
900
  // Fatal error
841
901
  const [fatalErrorMessage, setFatalErrorMessageInner,] = useState();
@@ -888,7 +948,7 @@ const AppWrapper = (props) => {
888
948
  if (fatalErrorMessage || fatalErrorCode || sessionHasExpired) {
889
949
  // Re-encapsulate in an error
890
950
  const error = (sessionHasExpired
891
- ? new ErrorWithCode(sessionExpiredMessage, ReactKitErrorCode$1.SessionExpired)
951
+ ? new ErrorWithCode(getSessionExpiredMessage(), ReactKitErrorCode$1.SessionExpired)
892
952
  : new ErrorWithCode((fatalErrorMessage !== null && fatalErrorMessage !== void 0 ? fatalErrorMessage : 'An unknown error has occurred. Please contact support.'), (fatalErrorCode !== null && fatalErrorCode !== void 0 ? fatalErrorCode : ReactKitErrorCode$1.NoCode)));
893
953
  // Build error screen
894
954
  body = (React.createElement("div", { style: {
@@ -896,7 +956,7 @@ const AppWrapper = (props) => {
896
956
  width: '100vw',
897
957
  minHeight: '100vh',
898
958
  paddingTop: '2rem',
899
- backgroundColor: (dark
959
+ backgroundColor: (darkModeIsOn()
900
960
  ? '#222'
901
961
  : '#fff'),
902
962
  } },
@@ -5525,6 +5585,54 @@ const canReviewLogs = () => __awaiter(void 0, void 0, void 0, function* () {
5525
5585
  return canReview;
5526
5586
  });
5527
5587
 
5588
+ /**
5589
+ * For every element in an array, extract the value of a prop
5590
+ * (e.g. for all user objects, extract their ages and put that into a new
5591
+ * ages array)
5592
+ * @author Gabe Abrams
5593
+ * @param arr the array of objects to operate on
5594
+ * @param prop the property to extract from each object
5595
+ * @returns new array containing the corresponding values, in order, of each
5596
+ * object in the original array
5597
+ */
5598
+ const extractProp = (arr, prop) => {
5599
+ return arr.map((item) => {
5600
+ return item[prop];
5601
+ });
5602
+ };
5603
+
5604
+ // Import shared helpers
5605
+ /**
5606
+ * Compare two arrays of objects by only comparing the values in a specific
5607
+ * property (e.g. compare user arrays by comparing their user.id values)
5608
+ * @author Gabe Abrams
5609
+ * @param a the first array
5610
+ * @param b the second array
5611
+ * @param prop the property to compare with
5612
+ * @returns true if the arrays contain the same objects as determined by
5613
+ * the values associated with each object's prop
5614
+ */
5615
+ const compareArraysByProp = (a, b, prop) => {
5616
+ // Extract values for comparison
5617
+ const aVals = new Set(extractProp(a, prop));
5618
+ const bVals = new Set(extractProp(b, prop));
5619
+ // Compare sizes first
5620
+ if (aVals.size !== bVals.size) {
5621
+ return false;
5622
+ }
5623
+ // Same number of items. Make sure every object in aVals appears in bVals
5624
+ // (if so, they should be equivalent since the sizes are the same)
5625
+ // > Create map of items in bVals
5626
+ const inBVals = {}; // item => true if in bVals
5627
+ Array.from(bVals.values()).forEach((item) => {
5628
+ inBVals[item] = true;
5629
+ });
5630
+ // > Loop through aVals and make sure every item is in bVals
5631
+ return Array.from(aVals.values()).every((item) => {
5632
+ return inBVals[item];
5633
+ });
5634
+ };
5635
+
5528
5636
  /**
5529
5637
  * Days of the week
5530
5638
  * @author Gabe Abrams
@@ -5541,5 +5649,5 @@ var DayOfWeek;
5541
5649
  })(DayOfWeek || (DayOfWeek = {}));
5542
5650
  var DayOfWeek$1 = DayOfWeek;
5543
5651
 
5544
- export { AppWrapper, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek$1 as DayOfWeek, Drawer, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, ItemPicker, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogReviewer, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, Variant$1 as Variant, abbreviate, alert$1 as alert, avg, canReviewLogs, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genCSV, genRouteHandler, getHumanReadableDate, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initLogCollection, initServer, isMobileOrTablet, logClientEvent, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
5652
+ export { AppWrapper, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek$1 as DayOfWeek, Drawer, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, ItemPicker, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogReviewer, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, Variant$1 as Variant, abbreviate, alert$1 as alert, avg, canReviewLogs, ceilToNumDecimals, compareArraysByProp, confirm, extractProp, floorToNumDecimals, forceNumIntoBounds, genCSV, genRouteHandler, getHumanReadableDate, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initClient, initLogCollection, initServer, isMobileOrTablet, logClientEvent, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
5545
5653
  //# sourceMappingURL=index.js.map