dce-reactkit 3.7.3 → 3.7.5

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 (46) hide show
  1. package/dist/cjs/index.js +159 -3
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/Modal.d.ts +1 -0
  4. package/dist/cjs/types/components/Shimmer.d.ts +13 -0
  5. package/dist/cjs/types/helpers/asyncArrayFunctions/everyAsync.d.ts +13 -0
  6. package/dist/cjs/types/helpers/asyncArrayFunctions/everyAsync.test.d.ts +1 -0
  7. package/dist/cjs/types/helpers/asyncArrayFunctions/filterAsync.d.ts +13 -0
  8. package/dist/cjs/types/helpers/asyncArrayFunctions/filterAsync.test.d.ts +1 -0
  9. package/dist/cjs/types/helpers/asyncArrayFunctions/forEachAsync.d.ts +10 -0
  10. package/dist/cjs/types/helpers/asyncArrayFunctions/forEachAsync.test.d.ts +1 -0
  11. package/dist/cjs/types/helpers/asyncArrayFunctions/mapAsync.d.ts +11 -0
  12. package/dist/cjs/types/helpers/asyncArrayFunctions/mapAsync.test.d.ts +1 -0
  13. package/dist/cjs/types/helpers/asyncArrayFunctions/someAsync.d.ts +13 -0
  14. package/dist/cjs/types/helpers/asyncArrayFunctions/someAsync.test.d.ts +1 -0
  15. package/dist/cjs/types/index.d.ts +6 -1
  16. package/dist/esm/index.js +155 -4
  17. package/dist/esm/index.js.map +1 -1
  18. package/dist/esm/types/components/Modal.d.ts +1 -0
  19. package/dist/esm/types/components/Shimmer.d.ts +13 -0
  20. package/dist/esm/types/helpers/asyncArrayFunctions/everyAsync.d.ts +13 -0
  21. package/dist/esm/types/helpers/asyncArrayFunctions/everyAsync.test.d.ts +1 -0
  22. package/dist/esm/types/helpers/asyncArrayFunctions/filterAsync.d.ts +13 -0
  23. package/dist/esm/types/helpers/asyncArrayFunctions/filterAsync.test.d.ts +1 -0
  24. package/dist/esm/types/helpers/asyncArrayFunctions/forEachAsync.d.ts +10 -0
  25. package/dist/esm/types/helpers/asyncArrayFunctions/forEachAsync.test.d.ts +1 -0
  26. package/dist/esm/types/helpers/asyncArrayFunctions/mapAsync.d.ts +11 -0
  27. package/dist/esm/types/helpers/asyncArrayFunctions/mapAsync.test.d.ts +1 -0
  28. package/dist/esm/types/helpers/asyncArrayFunctions/someAsync.d.ts +13 -0
  29. package/dist/esm/types/helpers/asyncArrayFunctions/someAsync.test.d.ts +1 -0
  30. package/dist/esm/types/index.d.ts +6 -1
  31. package/dist/index.d.ts +62 -1
  32. package/package.json +1 -1
  33. package/src/components/CopiableBox.tsx +1 -1
  34. package/src/components/Modal.tsx +4 -1
  35. package/src/components/Shimmer.tsx +153 -0
  36. package/src/helpers/asyncArrayFunctions/everyAsync.test.ts +114 -0
  37. package/src/helpers/asyncArrayFunctions/everyAsync.ts +51 -0
  38. package/src/helpers/asyncArrayFunctions/filterAsync.test.ts +126 -0
  39. package/src/helpers/asyncArrayFunctions/filterAsync.ts +50 -0
  40. package/src/helpers/asyncArrayFunctions/forEachAsync.test.ts +136 -0
  41. package/src/helpers/asyncArrayFunctions/forEachAsync.ts +40 -0
  42. package/src/helpers/asyncArrayFunctions/mapAsync.test.ts +125 -0
  43. package/src/helpers/asyncArrayFunctions/mapAsync.ts +46 -0
  44. package/src/helpers/asyncArrayFunctions/someAsync.test.ts +92 -0
  45. package/src/helpers/asyncArrayFunctions/someAsync.ts +49 -0
  46. package/src/index.ts +10 -0
@@ -14,6 +14,7 @@ type Props = {
14
14
  children?: React.ReactNode;
15
15
  onClose?: (type: ModalButtonType) => void;
16
16
  dontAllowBackdropExit?: boolean;
17
+ dontShowXButton?: boolean;
17
18
  okayLabel?: string;
18
19
  okayVariant?: Variant;
19
20
  cancelLabel?: string;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * A shimmer effect to add to the background of an element. The parent of this element
3
+ * will automatically have its overflow hidden
4
+ * @author Alessandra De Lucas
5
+ * @author Gabe Abrams
6
+ */
7
+ import React from 'react';
8
+ type Props = {
9
+ durationSec?: number;
10
+ numShimmers?: number;
11
+ };
12
+ declare const Shimmer: React.FC<Props>;
13
+ export default Shimmer;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Run the operator function on each item in the array, returning true if
3
+ * the operator function returns true for every item in the array
4
+ * @author Gabe Abrams
5
+ * @param operatorFunction the operator function to apply. If it returns true
6
+ * for every item, this function will return true
7
+ * @returns true if the operator function returns true for every item in the array
8
+ */
9
+ declare const everyAsync: <T>(array: T[], operatorFunction: (item: T, index: number, opts: {
10
+ breakNow: () => void;
11
+ array: T[];
12
+ }) => Promise<any>) => Promise<boolean>;
13
+ export default everyAsync;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Run the operator function on each item in the array, returning a new array
3
+ * that only contains the items that pass the filter
4
+ * @author Gabe Abrams
5
+ * @param operatorFunction the operator function to apply. If it returns true
6
+ * for an item, the item will be included in the returned array
7
+ * @returns the filtered array
8
+ */
9
+ declare const filterAsync: <T>(array: T[], operatorFunction: (item: T, index: number, opts: {
10
+ breakNow: () => void;
11
+ array: T[];
12
+ }) => Promise<any>) => Promise<T[]>;
13
+ export default filterAsync;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Run the operator function on each item in the array
3
+ * @author Gabe Abrams
4
+ * @param operatorFunction the operator function to apply
5
+ */
6
+ declare const forEachAsync: <T>(array: T[], operatorFunction: (item: T, index: number, opts: {
7
+ breakNow: () => void;
8
+ array: T[];
9
+ }) => void) => Promise<void>;
10
+ export default forEachAsync;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Run the operator function on each item in the array, collecting all results
3
+ * @author Gabe Abrams
4
+ * @param operatorFunction the operator function to apply
5
+ * @returns the array of results
6
+ */
7
+ declare const mapAsync: <T, U>(array: T[], operatorFunction: (item: T, index: number, opts: {
8
+ breakNow: () => void;
9
+ array: T[];
10
+ }) => Promise<U>) => Promise<U[]>;
11
+ export default mapAsync;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Run the operator function on each item in the array, returning true if
3
+ * the operator function returns true for any item in the array
4
+ * @author Gabe Abrams
5
+ * @param operatorFunction the operator function to apply. If it returns true
6
+ * for any item, this function will return true
7
+ * @returns true if the operator function returns true for any item in the array
8
+ */
9
+ declare const someAsync: <T>(array: T[], operatorFunction: (item: T, index: number, opts: {
10
+ breakNow: () => void;
11
+ array: T[];
12
+ }) => Promise<any>) => Promise<boolean>;
13
+ export default someAsync;
@@ -70,6 +70,11 @@ import makeLinksClickable from './helpers/makeLinksClickable';
70
70
  import combineClassNames from './helpers/combineClassNames';
71
71
  import prefixWithAOrAn from './helpers/prefixWithAOrAn';
72
72
  import useForceRender from './helpers/useForceRender';
73
+ import everyAsync from './helpers/asyncArrayFunctions/everyAsync';
74
+ import filterAsync from './helpers/asyncArrayFunctions/filterAsync';
75
+ import forEachAsync from './helpers/asyncArrayFunctions/forEachAsync';
76
+ import mapAsync from './helpers/asyncArrayFunctions/mapAsync';
77
+ import someAsync from './helpers/asyncArrayFunctions/someAsync';
73
78
  import ModalButtonType from './types/ModalButtonType';
74
79
  import ModalSize from './types/ModalSize';
75
80
  import ModalType from './types/ModalType';
@@ -89,4 +94,4 @@ import PickableItem from './components/ItemPicker/types/PickableItem';
89
94
  import DBEntry from './components/DBEntryManagerPanel/types/DBEntry';
90
95
  import DBEntryField from './components/DBEntryManagerPanel/types/DBEntryField';
91
96
  import DBEntryFieldType from './components/DBEntryManagerPanel/types/DBEntryFieldType';
92
- export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, DBEntryManagerPanel, Tooltip, ToggleSwitch, AutoscrollToBottomContainer, MultiSwitch, 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, genCommaList, validateEmail, validatePhoneNumber, validateString, getLocalTimeInfo, idify, makeLinksClickable, prefixWithAOrAn, initClient, visitServerEndpoint, logClientEvent, addFatalErrorHandler, leaveToURL, combineClassNames, useForceRender, setClientEventMetadataPopulator, initServer, genRouteHandler, handleError, handleSuccess, initLogCollection, addDBEditorEndpoints, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, LogFunction, IntelliTableColumn, PickableItem, DBEntry, DBEntryField, DBEntryFieldType, ParamType, };
97
+ export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, DBEntryManagerPanel, Tooltip, ToggleSwitch, AutoscrollToBottomContainer, MultiSwitch, 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, genCommaList, validateEmail, validatePhoneNumber, validateString, getLocalTimeInfo, idify, makeLinksClickable, prefixWithAOrAn, everyAsync, filterAsync, forEachAsync, mapAsync, someAsync, initClient, visitServerEndpoint, logClientEvent, addFatalErrorHandler, leaveToURL, combineClassNames, useForceRender, setClientEventMetadataPopulator, initServer, genRouteHandler, handleError, handleSuccess, initLogCollection, addDBEditorEndpoints, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, LogFunction, IntelliTableColumn, PickableItem, DBEntry, DBEntryField, DBEntryFieldType, ParamType, };
package/dist/index.d.ts CHANGED
@@ -153,6 +153,7 @@ type Props$j = {
153
153
  children?: React.ReactNode;
154
154
  onClose?: (type: ModalButtonType) => void;
155
155
  dontAllowBackdropExit?: boolean;
156
+ dontShowXButton?: boolean;
156
157
  okayLabel?: string;
157
158
  okayVariant?: Variant;
158
159
  cancelLabel?: string;
@@ -1465,6 +1466,66 @@ declare const prefixWithAOrAn: (text: string, capitalize?: boolean) => string;
1465
1466
  */
1466
1467
  declare const useForceRender: (useReducer: any) => () => void;
1467
1468
 
1469
+ /**
1470
+ * Run the operator function on each item in the array, returning true if
1471
+ * the operator function returns true for every item in the array
1472
+ * @author Gabe Abrams
1473
+ * @param operatorFunction the operator function to apply. If it returns true
1474
+ * for every item, this function will return true
1475
+ * @returns true if the operator function returns true for every item in the array
1476
+ */
1477
+ declare const everyAsync: <T>(array: T[], operatorFunction: (item: T, index: number, opts: {
1478
+ breakNow: () => void;
1479
+ array: T[];
1480
+ }) => Promise<any>) => Promise<boolean>;
1481
+
1482
+ /**
1483
+ * Run the operator function on each item in the array, returning a new array
1484
+ * that only contains the items that pass the filter
1485
+ * @author Gabe Abrams
1486
+ * @param operatorFunction the operator function to apply. If it returns true
1487
+ * for an item, the item will be included in the returned array
1488
+ * @returns the filtered array
1489
+ */
1490
+ declare const filterAsync: <T>(array: T[], operatorFunction: (item: T, index: number, opts: {
1491
+ breakNow: () => void;
1492
+ array: T[];
1493
+ }) => Promise<any>) => Promise<T[]>;
1494
+
1495
+ /**
1496
+ * Run the operator function on each item in the array
1497
+ * @author Gabe Abrams
1498
+ * @param operatorFunction the operator function to apply
1499
+ */
1500
+ declare const forEachAsync: <T>(array: T[], operatorFunction: (item: T, index: number, opts: {
1501
+ breakNow: () => void;
1502
+ array: T[];
1503
+ }) => void) => Promise<void>;
1504
+
1505
+ /**
1506
+ * Run the operator function on each item in the array, collecting all results
1507
+ * @author Gabe Abrams
1508
+ * @param operatorFunction the operator function to apply
1509
+ * @returns the array of results
1510
+ */
1511
+ declare const mapAsync: <T, U>(array: T[], operatorFunction: (item: T, index: number, opts: {
1512
+ breakNow: () => void;
1513
+ array: T[];
1514
+ }) => Promise<U>) => Promise<U[]>;
1515
+
1516
+ /**
1517
+ * Run the operator function on each item in the array, returning true if
1518
+ * the operator function returns true for any item in the array
1519
+ * @author Gabe Abrams
1520
+ * @param operatorFunction the operator function to apply. If it returns true
1521
+ * for any item, this function will return true
1522
+ * @returns true if the operator function returns true for any item in the array
1523
+ */
1524
+ declare const someAsync: <T>(array: T[], operatorFunction: (item: T, index: number, opts: {
1525
+ breakNow: () => void;
1526
+ array: T[];
1527
+ }) => Promise<any>) => Promise<boolean>;
1528
+
1468
1529
  /**
1469
1530
  * List of error codes built into the react kit
1470
1531
  * @author Gabe Abrams
@@ -1515,4 +1576,4 @@ declare const LogBuiltInMetadata: {
1515
1576
  };
1516
1577
  };
1517
1578
 
1518
- export { AppWrapper, AutoscrollToBottomContainer, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntry, DBEntryField, DBEntryFieldType, DBEntryManagerPanel, DayOfWeek, Drawer, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, IntelliTableColumn, ItemPicker, LoadingSpinner, Log, LogAction, LogBuiltInMetadata, LogFunction, LogMetadataType, LogReviewer, LogSource, LogType, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, MultiSwitch, ParamType, PickableItem, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, ToggleSwitch, Tooltip, Variant, abbreviate, addDBEditorEndpoints, addFatalErrorHandler, alert, avg, canReviewLogs, ceilToNumDecimals, combineClassNames, compareArraysByProp, confirm, extractProp, floorToNumDecimals, forceNumIntoBounds, genCSV, genCommaList, genRouteHandler, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, idify, initClient, initLogCollection, initServer, isMobileOrTablet, leaveToURL, logClientEvent, makeLinksClickable, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, roundToNumDecimals, setClientEventMetadataPopulator, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, useForceRender, validateEmail, validatePhoneNumber, validateString, visitServerEndpoint, waitMs };
1579
+ export { AppWrapper, AutoscrollToBottomContainer, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntry, DBEntryField, DBEntryFieldType, DBEntryManagerPanel, DayOfWeek, Drawer, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, IntelliTableColumn, ItemPicker, LoadingSpinner, Log, LogAction, LogBuiltInMetadata, LogFunction, LogMetadataType, LogReviewer, LogSource, LogType, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, MultiSwitch, ParamType, PickableItem, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, ToggleSwitch, Tooltip, Variant, abbreviate, addDBEditorEndpoints, addFatalErrorHandler, alert, avg, canReviewLogs, ceilToNumDecimals, combineClassNames, compareArraysByProp, confirm, everyAsync, extractProp, filterAsync, floorToNumDecimals, forEachAsync, forceNumIntoBounds, genCSV, genCommaList, genRouteHandler, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, idify, initClient, initLogCollection, initServer, isMobileOrTablet, leaveToURL, logClientEvent, makeLinksClickable, mapAsync, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, roundToNumDecimals, setClientEventMetadataPopulator, showFatalError, someAsync, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, useForceRender, validateEmail, validatePhoneNumber, validateString, visitServerEndpoint, waitMs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dce-reactkit",
3
- "version": "3.7.3",
3
+ "version": "3.7.5",
4
4
  "description": "Shared components for Harvard DCE apps",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -178,7 +178,7 @@ const CopiableBox: React.FC<Props> = (props) => {
178
178
  /*----------------------------------------*/
179
179
 
180
180
  return (
181
- <div className="input-group mb-2">
181
+ <div className="input-group">
182
182
  {/* Label */}
183
183
  {(label || labelIcon) && (
184
184
  <span
@@ -222,6 +222,8 @@ type Props = {
222
222
  onClose?: (type: ModalButtonType) => void,
223
223
  // If true, don't allow the user to click the backdrop to exit
224
224
  dontAllowBackdropExit?: boolean,
225
+ // If true, don't show the "X" close button
226
+ dontShowXButton?: boolean,
225
227
  // Custom label for "okay" button
226
228
  okayLabel?: string,
227
229
  // Custom variant for "okay" button
@@ -284,6 +286,7 @@ const Modal: React.FC<Props> = (props) => {
284
286
  children,
285
287
  onClose,
286
288
  dontAllowBackdropExit,
289
+ dontShowXButton,
287
290
  onTopOfOtherModals,
288
291
  } = props;
289
292
 
@@ -496,7 +499,7 @@ const Modal: React.FC<Props> = (props) => {
496
499
  {title}
497
500
  </h5>
498
501
 
499
- {onClose && (
502
+ {(onClose && !dontShowXButton) && (
500
503
  <button
501
504
  type="button"
502
505
  className="Modal-x-button btn-close"
@@ -0,0 +1,153 @@
1
+ /**
2
+ * A shimmer effect to add to the background of an element. The parent of this element
3
+ * will automatically have its overflow hidden
4
+ * @author Alessandra De Lucas
5
+ * @author Gabe Abrams
6
+ */
7
+
8
+ // Import React
9
+ import React, { useRef, useEffect } from 'react';
10
+
11
+ // Import helpers
12
+ import waitMs from '../helpers/waitMs';
13
+
14
+ /*------------------------------------------------------------------------*/
15
+ /* -------------------------------- Types ------------------------------- */
16
+ /*------------------------------------------------------------------------*/
17
+
18
+ // Props definition
19
+ type Props = {
20
+ // The duration in seconds of the shimmer animation
21
+ durationSec?: number,
22
+ // The number of iterations of the shimmer animation
23
+ numShimmers?: number,
24
+ };
25
+
26
+ /*------------------------------------------------------------------------*/
27
+ /* ------------------------------ Component ----------------------------- */
28
+ /*------------------------------------------------------------------------*/
29
+
30
+ const Shimmer: React.FC<Props> = (props) => {
31
+ /*------------------------------------------------------------------------*/
32
+ /* -------------------------------- Setup ------------------------------- */
33
+ /*------------------------------------------------------------------------*/
34
+
35
+ /* -------------- Props ------------- */
36
+
37
+ // Destructure all props
38
+ const {
39
+ durationSec = 2,
40
+ numShimmers,
41
+ } = props;
42
+
43
+ const numShimmerIterations: string = (
44
+ numShimmers
45
+ ? String(numShimmers)
46
+ : 'infinite'
47
+ );
48
+
49
+ /* --------------- Refs ------------- */
50
+
51
+ // Ref for the shimmer container
52
+ const containerRef = useRef<HTMLDivElement>(null);
53
+
54
+ /*------------------------------------------------------------------------*/
55
+ /* -------------------------------- Style ------------------------------- */
56
+ /*------------------------------------------------------------------------*/
57
+
58
+ // Style
59
+ const style = `
60
+ .Shimmer-container {
61
+ display: inline-block;
62
+ position: absolute;
63
+ left: 0;
64
+ top: 0;
65
+ width: 100%;
66
+ height: 0;
67
+ overflow: visible;
68
+ }
69
+
70
+ .Shimmer-shine {
71
+ display: inline-block;
72
+ position: absolute;
73
+ width: 100%;
74
+ height: 100vh;
75
+
76
+ background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0));
77
+
78
+ animation-name: Shimmer-slide-animation;
79
+ animation-duration: ${durationSec}s;
80
+ animation-iteration-count: ${numShimmerIterations};
81
+ animation-timing-function: ease-in-out;
82
+ animation-fill-mode: both;
83
+ }
84
+
85
+ @keyframes Shimmer-slide-animation {
86
+ 0% {
87
+ transform: translateX(-100%);
88
+ }
89
+ 100% {
90
+ transform: translateX(100%);
91
+ }
92
+ }
93
+ `;
94
+
95
+ /*------------------------------------------------------------------------*/
96
+ /* ------------------------- Lifecycle Functions ------------------------ */
97
+ /*------------------------------------------------------------------------*/
98
+
99
+ /**
100
+ * Mount: force parent to have its overflow hidden
101
+ * @author Gabe Abrams
102
+ */
103
+ useEffect(
104
+ () => {
105
+ (async () => {
106
+ // Wait for the container to exist
107
+ while (!containerRef.current) {
108
+ await waitMs(20);
109
+ }
110
+
111
+ // Find the parent
112
+ const parent = containerRef.current.parentElement;
113
+ if (!parent) {
114
+ return;
115
+ }
116
+
117
+ // Force the parent's overflow to be hidden
118
+ parent.style.overflow = 'hidden';
119
+ })();
120
+ },
121
+ [],
122
+ );
123
+
124
+ /*------------------------------------------------------------------------*/
125
+ /* ------------------------------- Render ------------------------------- */
126
+ /*------------------------------------------------------------------------*/
127
+
128
+ /*----------------------------------------*/
129
+ /* --------------- Main UI -------------- */
130
+ /*----------------------------------------*/
131
+
132
+ return (
133
+ <div
134
+ className="Shimmer-container"
135
+ ref={containerRef}
136
+ >
137
+ {/* Style */}
138
+ <style>
139
+ {style}
140
+ </style>
141
+
142
+ {/* Moving shimmer */}
143
+ <div className="Shimmer-shine" />
144
+ </div>
145
+ );
146
+ };
147
+
148
+ /*------------------------------------------------------------------------*/
149
+ /* ------------------------------- Wrap Up ------------------------------ */
150
+ /*------------------------------------------------------------------------*/
151
+
152
+ // Export component
153
+ export default Shimmer;
@@ -0,0 +1,114 @@
1
+ // Import item to test
2
+ import everyAsync from './everyAsync';
3
+
4
+ describe('everyAsync', () => {
5
+ it('returns true for an empty array', async () => {
6
+ const result = await everyAsync(
7
+ [],
8
+ async () => {
9
+ return true;
10
+ },
11
+ );
12
+ expect(result).toBe(true);
13
+ });
14
+
15
+ it('returns true if the operator function returns true for every item in the array', async () => {
16
+ const result = await everyAsync(
17
+ [1, 2, 3],
18
+ async (item) => {
19
+ return item > 0;
20
+ },
21
+ );
22
+ expect(result).toBe(true);
23
+ });
24
+
25
+ it('returns false if the operator function returns false for any item in the array', async () => {
26
+ const result = await everyAsync(
27
+ [1, 2, 3],
28
+ async (item) => {
29
+ return item > 1;
30
+ },
31
+ );
32
+ expect(result).toBe(false);
33
+ });
34
+
35
+ it('passes the index as the second argument to the operator function', async () => {
36
+ const result = await everyAsync(
37
+ [1, 2, 3],
38
+ async (item, index) => {
39
+ return index === item - 1;
40
+ },
41
+ );
42
+ expect(result).toBe(true);
43
+ });
44
+
45
+ it('passes an object with a breakNow function and the array as the third argument to the operator function', async () => {
46
+ const result = await everyAsync(
47
+ [1, 2, 3],
48
+ async (item, index, { breakNow }) => {
49
+ if (item === 2) {
50
+ breakNow();
51
+ }
52
+ return true;
53
+ },
54
+ );
55
+ expect(result).toBe(false);
56
+ });
57
+
58
+ it('stops iterating if the operator function calls the breakNow function', async () => {
59
+ const mockOperatorFunction = jest.fn(async (item, index, { breakNow }) => {
60
+ if (item === 2) {
61
+ breakNow();
62
+ }
63
+ return true;
64
+ });
65
+ const result = await everyAsync(
66
+ [1, 2, 3],
67
+ mockOperatorFunction,
68
+ );
69
+ expect(result).toBe(false);
70
+ expect(mockOperatorFunction).toHaveBeenCalledTimes(2);
71
+ });
72
+
73
+ it('handles an operator function that returns a promise that resolves to true', async () => {
74
+ const result = await everyAsync(
75
+ [1, 2, 3],
76
+ async (item) => {
77
+ return Promise.resolve(item > 0);
78
+ },
79
+ );
80
+ expect(result).toBe(true);
81
+ });
82
+
83
+ it('handles an operator function that returns a promise that resolves to false', async () => {
84
+ const result = await everyAsync(
85
+ [1, 2, 3],
86
+ async (item) => {
87
+ return Promise.resolve(item > 1);
88
+ },
89
+ );
90
+ expect(result).toBe(false);
91
+ });
92
+
93
+ it('handles an operator function that throws an error', async () => {
94
+ const mockOperatorFunction = jest.fn(async (item) => {
95
+ if (item === 2) {
96
+ throw new Error('Test error');
97
+ }
98
+ return true;
99
+ });
100
+ let error: any = null;
101
+ let result: boolean | null = null;
102
+ try {
103
+ result = await everyAsync(
104
+ [1, 2, 3],
105
+ mockOperatorFunction,
106
+ );
107
+ } catch (err) {
108
+ error = err;
109
+ }
110
+ expect(result).toBe(null);
111
+ expect(error).not.toBe(null);
112
+ expect(mockOperatorFunction).toHaveBeenCalledTimes(2);
113
+ });
114
+ });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Run the operator function on each item in the array, returning true if
3
+ * the operator function returns true for every item in the array
4
+ * @author Gabe Abrams
5
+ * @param operatorFunction the operator function to apply. If it returns true
6
+ * for every item, this function will return true
7
+ * @returns true if the operator function returns true for every item in the array
8
+ */
9
+ const everyAsync = async <T>(
10
+ array: T[],
11
+ operatorFunction: (
12
+ item: T,
13
+ index: number,
14
+ opts: {
15
+ breakNow: () => void,
16
+ array: Array<T>,
17
+ },
18
+ ) => Promise<any>,
19
+ ): Promise<boolean> => {
20
+ // Create break logic
21
+ let done = false;
22
+ /**
23
+ * Break the loop (checking stops here)
24
+ * @author Gabe Abrams
25
+ */
26
+ const breakNow = () => {
27
+ done = true;
28
+ };
29
+
30
+ // Loop through each item, checking
31
+ for (let i = 0; i < array.length && !done; i++) {
32
+ const passed = await operatorFunction(
33
+ array[i],
34
+ i,
35
+ {
36
+ breakNow,
37
+ array,
38
+ },
39
+ );
40
+
41
+ // Check if this one failed or if loop was broken
42
+ if (!passed || done) {
43
+ return false;
44
+ }
45
+ }
46
+
47
+ // Return true because none returned false
48
+ return true;
49
+ };
50
+
51
+ export default everyAsync;