dce-reactkit 3.0.0-beta.4 → 3.0.0-beta.40
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.
- package/.eslintrc.js +95 -0
- package/README.md +1 -546
- package/dist/cjs/index.js +784 -85
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/AppWrapper.d.ts +25 -2
- package/dist/cjs/types/components/ButtonInputGroup.d.ts +12 -0
- package/dist/cjs/types/components/CheckboxButton.d.ts +20 -0
- package/dist/cjs/types/components/RadioButton.d.ts +20 -0
- package/dist/cjs/types/components/SimpleDateChooser.d.ts +21 -0
- package/dist/cjs/types/components/TabBox.d.ts +1 -0
- package/dist/cjs/types/helpers/genRouteHandler.d.ts +30 -0
- package/dist/cjs/types/helpers/getOrdinal.d.ts +8 -0
- package/dist/cjs/types/helpers/getTimeInfoInET.d.ts +17 -0
- package/dist/cjs/types/helpers/handleError.d.ts +18 -0
- package/dist/cjs/types/helpers/handleSuccess.d.ts +8 -0
- package/dist/cjs/types/helpers/visitServerEndpoint.d.ts +23 -0
- package/dist/cjs/types/index.d.ts +10 -1
- package/dist/cjs/types/server/initServer.d.ts +21 -0
- package/dist/cjs/types/types/ParamType.d.ts +17 -0
- package/dist/cjs/types/types/ReactKitErrorCode.d.ts +3 -1
- package/dist/esm/index.js +776 -86
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/AppWrapper.d.ts +25 -2
- package/dist/esm/types/components/ButtonInputGroup.d.ts +12 -0
- package/dist/esm/types/components/CheckboxButton.d.ts +20 -0
- package/dist/esm/types/components/RadioButton.d.ts +20 -0
- package/dist/esm/types/components/SimpleDateChooser.d.ts +21 -0
- package/dist/esm/types/components/TabBox.d.ts +1 -0
- package/dist/esm/types/helpers/genRouteHandler.d.ts +30 -0
- package/dist/esm/types/helpers/getOrdinal.d.ts +8 -0
- package/dist/esm/types/helpers/getTimeInfoInET.d.ts +17 -0
- package/dist/esm/types/helpers/handleError.d.ts +18 -0
- package/dist/esm/types/helpers/handleSuccess.d.ts +8 -0
- package/dist/esm/types/helpers/visitServerEndpoint.d.ts +23 -0
- package/dist/esm/types/index.d.ts +10 -1
- package/dist/esm/types/server/initServer.d.ts +21 -0
- package/dist/esm/types/types/ParamType.d.ts +17 -0
- package/dist/esm/types/types/ReactKitErrorCode.d.ts +3 -1
- package/dist/index.d.ts +182 -10
- package/package.json +13 -1
- package/rollup.config.js +0 -1
- package/src/components/AppWrapper.tsx +73 -15
- package/src/components/ButtonInputGroup.tsx +89 -0
- package/src/components/CheckboxButton.tsx +100 -0
- package/src/components/ErrorBox.tsx +4 -4
- package/src/components/LoadingSpinner.tsx +3 -3
- package/src/components/Modal.tsx +185 -71
- package/src/components/RadioButton.tsx +102 -0
- package/src/components/SimpleDateChooser.tsx +217 -0
- package/src/components/TabBox.tsx +65 -58
- package/src/helpers/genRouteHandler.ts +326 -0
- package/src/helpers/getOrdinal.tsx +13 -0
- package/src/helpers/getTimeInfoInET.tsx +56 -0
- package/src/helpers/handleError.ts +65 -0
- package/src/helpers/handleSuccess.ts +18 -0
- package/src/helpers/visitServerEndpoint.tsx +110 -0
- package/src/index.ts +21 -0
- package/src/server/initServer.ts +53 -0
- package/src/types/ParamType.ts +18 -0
- package/src/types/ReactKitErrorCode.tsx +3 -1
package/dist/index.d.ts
CHANGED
|
@@ -7,11 +7,29 @@ import React from 'react';
|
|
|
7
7
|
* @author Gabe Abrams
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
declare type Props$
|
|
10
|
+
declare type Props$6 = {
|
|
11
11
|
children: React.ReactNode;
|
|
12
|
+
sendRequest: SendRequestFunction;
|
|
12
13
|
dark?: boolean;
|
|
13
14
|
sessionExpiredMessage?: string;
|
|
14
15
|
};
|
|
16
|
+
declare type SendRequestFunction = (opts: {
|
|
17
|
+
path: string;
|
|
18
|
+
method: ('GET' | 'POST' | 'DELETE' | 'PUT');
|
|
19
|
+
params?: {
|
|
20
|
+
[x: string]: any;
|
|
21
|
+
} | undefined;
|
|
22
|
+
headers?: {
|
|
23
|
+
[x: string]: any;
|
|
24
|
+
} | undefined;
|
|
25
|
+
numRetries?: number | undefined;
|
|
26
|
+
}) => Promise<{
|
|
27
|
+
body: any;
|
|
28
|
+
status: number;
|
|
29
|
+
headers: {
|
|
30
|
+
[x: string]: any;
|
|
31
|
+
};
|
|
32
|
+
}>;
|
|
15
33
|
/**
|
|
16
34
|
* Show an alert modal with an "Okay" button
|
|
17
35
|
* @author Gabe Abrams
|
|
@@ -34,7 +52,7 @@ declare const confirm: (title: string, text: string) => Promise<boolean>;
|
|
|
34
52
|
* @param [errorTitle] title of the error box
|
|
35
53
|
*/
|
|
36
54
|
declare const showFatalError: (error: any, errorTitle?: string) => undefined;
|
|
37
|
-
declare const AppWrapper: React.FC<Props$
|
|
55
|
+
declare const AppWrapper: React.FC<Props$6>;
|
|
38
56
|
|
|
39
57
|
/**
|
|
40
58
|
* Loading spinner/indicator
|
|
@@ -47,12 +65,12 @@ declare const LoadingSpinner: () => JSX.Element;
|
|
|
47
65
|
* @author Gabe Abrams
|
|
48
66
|
*/
|
|
49
67
|
|
|
50
|
-
declare type Props$
|
|
68
|
+
declare type Props$5 = {
|
|
51
69
|
error: any;
|
|
52
70
|
title?: string;
|
|
53
71
|
onClose?: () => void;
|
|
54
72
|
};
|
|
55
|
-
declare const ErrorBox: React.FC<Props$
|
|
73
|
+
declare const ErrorBox: React.FC<Props$5>;
|
|
56
74
|
|
|
57
75
|
/**
|
|
58
76
|
* Bootstrap variants
|
|
@@ -118,7 +136,7 @@ declare enum ModalType {
|
|
|
118
136
|
* @author Gabe Abrams
|
|
119
137
|
*/
|
|
120
138
|
|
|
121
|
-
declare type Props$
|
|
139
|
+
declare type Props$4 = {
|
|
122
140
|
type?: ModalType;
|
|
123
141
|
size?: ModalSize;
|
|
124
142
|
title?: React.ReactNode;
|
|
@@ -147,18 +165,69 @@ declare type Props$1 = {
|
|
|
147
165
|
confirmVariant?: Variant;
|
|
148
166
|
onTopOfOtherModals?: boolean;
|
|
149
167
|
};
|
|
150
|
-
declare const Modal: React.FC<Props$
|
|
168
|
+
declare const Modal: React.FC<Props$4>;
|
|
151
169
|
|
|
152
170
|
/**
|
|
153
171
|
* A box with a tab on the top that holds buttons and other content
|
|
154
172
|
* @author Gabe Abrams
|
|
155
173
|
*/
|
|
156
174
|
|
|
157
|
-
declare type Props = {
|
|
175
|
+
declare type Props$3 = {
|
|
158
176
|
title: React.ReactNode;
|
|
159
177
|
children: React.ReactNode;
|
|
178
|
+
noBottomPadding?: boolean;
|
|
179
|
+
};
|
|
180
|
+
declare const TabBox: React.FC<Props$3>;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* A radio selection button
|
|
184
|
+
* @author Gabe Abrams
|
|
185
|
+
*/
|
|
186
|
+
|
|
187
|
+
declare type Props$2 = {
|
|
188
|
+
text: string;
|
|
189
|
+
onSelected: () => void;
|
|
190
|
+
ariaLabel: string;
|
|
191
|
+
title?: string;
|
|
192
|
+
selected?: boolean;
|
|
193
|
+
id?: string;
|
|
194
|
+
noMarginOnRight?: boolean;
|
|
195
|
+
selectedVariant?: Variant;
|
|
196
|
+
unselectedVariant?: Variant;
|
|
197
|
+
small?: boolean;
|
|
198
|
+
};
|
|
199
|
+
declare const RadioButton: React.FC<Props$2>;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* A checkbox button
|
|
203
|
+
* @author Gabe Abrams
|
|
204
|
+
*/
|
|
205
|
+
|
|
206
|
+
declare type Props$1 = {
|
|
207
|
+
text: string;
|
|
208
|
+
onChanged: (checked: boolean) => void;
|
|
209
|
+
ariaLabel: string;
|
|
210
|
+
title?: string;
|
|
211
|
+
checked?: boolean;
|
|
212
|
+
id?: string;
|
|
213
|
+
noMarginOnRight?: boolean;
|
|
214
|
+
checkedVariant?: Variant;
|
|
215
|
+
uncheckedVariant?: Variant;
|
|
216
|
+
small?: boolean;
|
|
160
217
|
};
|
|
161
|
-
declare const
|
|
218
|
+
declare const CheckboxButton: React.FC<Props$1>;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Input group with a title and space for buttons
|
|
222
|
+
* @author Gabe Abrams
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
declare type Props = {
|
|
226
|
+
label: string;
|
|
227
|
+
minLabelWidth?: string;
|
|
228
|
+
children: React.ReactNode;
|
|
229
|
+
};
|
|
230
|
+
declare const ButtonInputGroup: React.FC<Props>;
|
|
162
231
|
|
|
163
232
|
/**
|
|
164
233
|
* An error with a code
|
|
@@ -258,6 +327,107 @@ declare const sum: (nums: number[]) => number;
|
|
|
258
327
|
*/
|
|
259
328
|
declare const waitMs: (ms?: number) => Promise<unknown>;
|
|
260
329
|
|
|
330
|
+
/**
|
|
331
|
+
* Visit an endpoint on the server [for client only]
|
|
332
|
+
* @author Gabe Abrams
|
|
333
|
+
* @param opts object containing all arguments
|
|
334
|
+
* @param opts.path - the path of the server endpoint
|
|
335
|
+
* @param [opts.method=GET] - the method of the endpoint
|
|
336
|
+
* @param [opts.params] - query/body parameters to include
|
|
337
|
+
* @returns response from server
|
|
338
|
+
*/
|
|
339
|
+
declare const visitServerEndpoint: (opts: {
|
|
340
|
+
path: string;
|
|
341
|
+
method?: "GET" | "POST" | "DELETE" | "PUT" | undefined;
|
|
342
|
+
params?: {
|
|
343
|
+
[x: string]: any;
|
|
344
|
+
} | undefined;
|
|
345
|
+
}) => Promise<any>;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Server-side API param types
|
|
349
|
+
* @author Gabe Abrams
|
|
350
|
+
*/
|
|
351
|
+
declare enum ParamType {
|
|
352
|
+
Boolean = "boolean",
|
|
353
|
+
BooleanOptional = "boolean-optional",
|
|
354
|
+
Float = "float",
|
|
355
|
+
FloatOptional = "float-optional",
|
|
356
|
+
Int = "int",
|
|
357
|
+
IntOptional = "int-optional",
|
|
358
|
+
JSON = "json",
|
|
359
|
+
JSONOptional = "json-optional",
|
|
360
|
+
String = "string",
|
|
361
|
+
StringOptional = "string-optional"
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Handle an error and respond to the client
|
|
366
|
+
* @author Gabe Abrams
|
|
367
|
+
* @param res express response
|
|
368
|
+
* @param error error info
|
|
369
|
+
* @param opts.err the error to send to the client
|
|
370
|
+
* or the error message
|
|
371
|
+
* @param [opts.code] an error code (only used if err.code is not
|
|
372
|
+
* included)
|
|
373
|
+
* @param [opts.status=500] the https status code to use
|
|
374
|
+
* defined)
|
|
375
|
+
*/
|
|
376
|
+
declare const handleError: (res: any, error: ({
|
|
377
|
+
message: any;
|
|
378
|
+
code?: string;
|
|
379
|
+
status?: number;
|
|
380
|
+
} | Error | string | any)) => undefined;
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Send successful API response
|
|
384
|
+
* @author Gabe Abrams
|
|
385
|
+
* @param res express response
|
|
386
|
+
* @param body the body of the response to send to the client
|
|
387
|
+
*/
|
|
388
|
+
declare const handleSuccess: (res: any, body: any) => undefined;
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Generate an express API route handler
|
|
392
|
+
* @author Gabe Abrams
|
|
393
|
+
* @param opts object containing all arguments
|
|
394
|
+
* @param opts.paramTypes map containing the types for each parameter that is
|
|
395
|
+
* included in the request (map: param name => type)
|
|
396
|
+
* @param opts.handler function that processes the request
|
|
397
|
+
* @returns express route handler that takes the following arguments:
|
|
398
|
+
* params (map: param name => value), handleSuccess (function for handling
|
|
399
|
+
* successful requests), handleError (function for handling failed requests),
|
|
400
|
+
* req (express request object), res (express response object)
|
|
401
|
+
*/
|
|
402
|
+
declare const genRouteHandler: (opts: {
|
|
403
|
+
paramTypes?: {
|
|
404
|
+
[k: string]: ParamType;
|
|
405
|
+
} | undefined;
|
|
406
|
+
handler: (opts: {
|
|
407
|
+
params: {
|
|
408
|
+
[k: string]: any;
|
|
409
|
+
};
|
|
410
|
+
handleSuccess: (body: any) => void;
|
|
411
|
+
handleError: (error: any) => void;
|
|
412
|
+
req: any;
|
|
413
|
+
res: any;
|
|
414
|
+
}) => void;
|
|
415
|
+
}) => (req: any, res: any) => Promise<undefined>;
|
|
416
|
+
|
|
417
|
+
declare type GetLaunchInfoFunction = (req: any) => {
|
|
418
|
+
launched: boolean;
|
|
419
|
+
launchInfo?: any;
|
|
420
|
+
};
|
|
421
|
+
/**
|
|
422
|
+
* Prepare dce-reactkit to run on the server
|
|
423
|
+
* @author Gabe Abrams
|
|
424
|
+
* @param opts object containing all arguments
|
|
425
|
+
* @param opts.getLaunchInfo CACCL LTI's get launch info function
|
|
426
|
+
*/
|
|
427
|
+
declare const initServer: (opts: {
|
|
428
|
+
getLaunchInfo: GetLaunchInfoFunction;
|
|
429
|
+
}) => void;
|
|
430
|
+
|
|
261
431
|
/**
|
|
262
432
|
* List of error codes built into the react kit
|
|
263
433
|
* @author Gabe Abrams
|
|
@@ -268,7 +438,9 @@ declare enum ReactKitErrorCode {
|
|
|
268
438
|
SessionExpired = "DRK3",
|
|
269
439
|
MissingParameter = "DRK4",
|
|
270
440
|
InvalidParameter = "DRK5",
|
|
271
|
-
WrongCourse = "DRK6"
|
|
441
|
+
WrongCourse = "DRK6",
|
|
442
|
+
NoCACCLSendRequestFunction = "DRK7",
|
|
443
|
+
NoCACCLGetLaunchInfoFunction = "DRK8"
|
|
272
444
|
}
|
|
273
445
|
|
|
274
|
-
export { AppWrapper, ErrorBox, ErrorWithCode, LoadingSpinner, Modal, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, TabBox, Variant, abbreviate, alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, showFatalError, sum, waitMs };
|
|
446
|
+
export { AppWrapper, ButtonInputGroup, CheckboxButton, ErrorBox, ErrorWithCode, LoadingSpinner, Modal, ModalButtonType, ModalSize, ModalType, ParamType, RadioButton, ReactKitErrorCode, TabBox, Variant, abbreviate, alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genRouteHandler, handleError, handleSuccess, initServer, padDecimalZeros, padZerosLeft, roundToNumDecimals, showFatalError, sum, visitServerEndpoint, waitMs };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dce-reactkit",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.40",
|
|
4
4
|
"description": "Shared components for Harvard DCE apps",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -19,15 +19,27 @@
|
|
|
19
19
|
},
|
|
20
20
|
"homepage": "https://github.com/harvard-edtech/dce-reactkit#readme",
|
|
21
21
|
"peerDependencies": {
|
|
22
|
+
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
|
22
23
|
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
|
23
24
|
"@fortawesome/react-fontawesome": "^0.1.18",
|
|
25
|
+
"bootstrap": "^5.1.3",
|
|
24
26
|
"react": "^18.0.0"
|
|
25
27
|
},
|
|
26
28
|
"devDependencies": {
|
|
27
29
|
"@rollup/plugin-commonjs": "^22.0.0",
|
|
30
|
+
"@rollup/plugin-json": "^4.1.0",
|
|
28
31
|
"@rollup/plugin-node-resolve": "^13.2.1",
|
|
29
32
|
"@rollup/plugin-typescript": "^8.3.2",
|
|
30
33
|
"@types/react": "^18.0.7",
|
|
34
|
+
"@typescript-eslint/eslint-plugin": "^5.21.0",
|
|
35
|
+
"@typescript-eslint/parser": "^5.21.0",
|
|
36
|
+
"eslint": "^8.14.0",
|
|
37
|
+
"eslint-config-airbnb": "^19.0.4",
|
|
38
|
+
"eslint-config-airbnb-typescript": "^17.0.0",
|
|
39
|
+
"eslint-plugin-import": "^2.26.0",
|
|
40
|
+
"eslint-plugin-jsx-a11y": "^6.5.1",
|
|
41
|
+
"eslint-plugin-react": "^7.29.4",
|
|
42
|
+
"eslint-plugin-react-hooks": "^4.4.0",
|
|
31
43
|
"rollup": "^2.70.2",
|
|
32
44
|
"rollup-plugin-dts": "^4.2.1",
|
|
33
45
|
"rollup-plugin-sourcemaps": "^0.6.3",
|
package/rollup.config.js
CHANGED
|
@@ -7,10 +7,6 @@
|
|
|
7
7
|
// Import React
|
|
8
8
|
import React, { useState } from 'react';
|
|
9
9
|
|
|
10
|
-
// Import bootstrap stylesheet and javascript
|
|
11
|
-
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
12
|
-
import 'bootstrap/dist/js/bootstrap.min';
|
|
13
|
-
|
|
14
10
|
// Import shared components
|
|
15
11
|
import ErrorBox from './ErrorBox';
|
|
16
12
|
|
|
@@ -32,22 +28,70 @@ import ErrorWithCode from '../errors/ErrorWithCode';
|
|
|
32
28
|
type Props = {
|
|
33
29
|
// The entire app
|
|
34
30
|
children: React.ReactNode,
|
|
31
|
+
// Copy of CACCL's send request function
|
|
32
|
+
sendRequest: SendRequestFunction,
|
|
35
33
|
// True if this app is a dark-themed app
|
|
36
34
|
dark?: boolean,
|
|
37
35
|
// Custom session expired message
|
|
38
36
|
sessionExpiredMessage?: string,
|
|
39
37
|
};
|
|
40
38
|
|
|
39
|
+
// Type of CACCL's send request function
|
|
40
|
+
type SendRequestFunction = (
|
|
41
|
+
opts: {
|
|
42
|
+
path: string;
|
|
43
|
+
method: ('GET' | 'POST' | 'DELETE' | 'PUT');
|
|
44
|
+
params?: {
|
|
45
|
+
[x: string]: any;
|
|
46
|
+
} | undefined;
|
|
47
|
+
headers?: {
|
|
48
|
+
[x: string]: any;
|
|
49
|
+
} | undefined;
|
|
50
|
+
numRetries?: number | undefined;
|
|
51
|
+
},
|
|
52
|
+
) => Promise<{
|
|
53
|
+
body: any;
|
|
54
|
+
status: number;
|
|
55
|
+
headers: {
|
|
56
|
+
[x: string]: any;
|
|
57
|
+
};
|
|
58
|
+
}>;
|
|
59
|
+
|
|
41
60
|
/*------------------------------------------------------------------------*/
|
|
42
61
|
/* Static Helpers */
|
|
43
62
|
/*------------------------------------------------------------------------*/
|
|
44
63
|
|
|
64
|
+
/*----------------------------------------*/
|
|
65
|
+
/* Send Request */
|
|
66
|
+
/*----------------------------------------*/
|
|
67
|
+
|
|
68
|
+
// Store copy of caccl send request
|
|
69
|
+
let _cacclSendRequest: SendRequestFunction;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Send a request using caccl's send request feature
|
|
73
|
+
* @author Gabe Abrams
|
|
74
|
+
* @param opts send request options
|
|
75
|
+
* @returns send request response
|
|
76
|
+
*/
|
|
77
|
+
export const cacclSendRequest: SendRequestFunction = async (opts) => {
|
|
78
|
+
// Make sure send request has been passed in
|
|
79
|
+
if (!_cacclSendRequest) {
|
|
80
|
+
throw new ErrorWithCode(
|
|
81
|
+
'The request could not be sent because the AppWrapper component does not have a copy of sendRequest from CACCL.',
|
|
82
|
+
ReactKitErrorCode.NoCACCLSendRequestFunction,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return _cacclSendRequest(opts);
|
|
87
|
+
};
|
|
88
|
+
|
|
45
89
|
/*----------------------------------------*/
|
|
46
90
|
/* Alert */
|
|
47
91
|
/*----------------------------------------*/
|
|
48
92
|
|
|
49
93
|
// Stored copies of setters
|
|
50
|
-
let setAlertInfo: (info: { title: string, text: string }) => void;
|
|
94
|
+
let setAlertInfo: (info: undefined | { title: string, text: string }) => void;
|
|
51
95
|
let onAlertClosed: () => void;
|
|
52
96
|
|
|
53
97
|
/**
|
|
@@ -83,7 +127,7 @@ export const alert = async (title: string, text: string): Promise<undefined> =>
|
|
|
83
127
|
/*----------------------------------------*/
|
|
84
128
|
|
|
85
129
|
// Stored copies of setters
|
|
86
|
-
let setConfirmInfo: (info: { title: string, text: string }) => void;
|
|
130
|
+
let setConfirmInfo: (info: undefined | { title: string, text: string }) => void;
|
|
87
131
|
let onConfirmClosed: (confirmed: boolean) => void;
|
|
88
132
|
|
|
89
133
|
/**
|
|
@@ -194,10 +238,14 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
|
|
|
194
238
|
|
|
195
239
|
const {
|
|
196
240
|
children,
|
|
241
|
+
sendRequest,
|
|
197
242
|
dark,
|
|
198
243
|
sessionExpiredMessage = 'Your session has expired. Please go back to Canvas and start over.',
|
|
199
244
|
} = props;
|
|
200
245
|
|
|
246
|
+
// Store copy of send request
|
|
247
|
+
_cacclSendRequest = sendRequest;
|
|
248
|
+
|
|
201
249
|
/* -------------- State ------------- */
|
|
202
250
|
|
|
203
251
|
// Fatal error
|
|
@@ -221,20 +269,26 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
|
|
|
221
269
|
const [
|
|
222
270
|
alertInfo,
|
|
223
271
|
setAlertInfoInner,
|
|
224
|
-
] = useState<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
272
|
+
] = useState<
|
|
273
|
+
undefined
|
|
274
|
+
| {
|
|
275
|
+
title: string,
|
|
276
|
+
text: string
|
|
277
|
+
}
|
|
278
|
+
>(undefined);
|
|
228
279
|
setAlertInfo = setAlertInfoInner;
|
|
229
280
|
|
|
230
281
|
// Confirm
|
|
231
282
|
const [
|
|
232
283
|
confirmInfo,
|
|
233
284
|
setConfirmInfoInner,
|
|
234
|
-
] = useState<
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
285
|
+
] = useState<
|
|
286
|
+
undefined
|
|
287
|
+
| {
|
|
288
|
+
title: string,
|
|
289
|
+
text: string
|
|
290
|
+
}
|
|
291
|
+
>(undefined);
|
|
238
292
|
setConfirmInfo = setConfirmInfoInner;
|
|
239
293
|
|
|
240
294
|
// Session expired
|
|
@@ -260,10 +314,12 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
|
|
|
260
314
|
if (alertInfo) {
|
|
261
315
|
modal = (
|
|
262
316
|
<Modal
|
|
317
|
+
key={`alert-${alertInfo.title}-${alertInfo.text}`}
|
|
263
318
|
title={alertInfo.title}
|
|
264
319
|
type={ModalType.Okay}
|
|
265
320
|
onClose={() => {
|
|
266
321
|
// Alert closed
|
|
322
|
+
setAlertInfo(undefined);
|
|
267
323
|
if (onAlertClosed) {
|
|
268
324
|
onAlertClosed();
|
|
269
325
|
}
|
|
@@ -280,16 +336,18 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
|
|
|
280
336
|
if (confirmInfo) {
|
|
281
337
|
modal = (
|
|
282
338
|
<Modal
|
|
339
|
+
key={`confirm-${confirmInfo.title}-${confirmInfo.text}`}
|
|
283
340
|
title={confirmInfo.title}
|
|
284
341
|
type={ModalType.OkayCancel}
|
|
285
342
|
onClose={(buttonType) => {
|
|
343
|
+
setConfirmInfo(undefined);
|
|
286
344
|
if (onConfirmClosed) {
|
|
287
345
|
onConfirmClosed(buttonType === ModalButtonType.Okay);
|
|
288
346
|
}
|
|
289
347
|
}}
|
|
290
348
|
dontAllowBackdropExit
|
|
291
349
|
>
|
|
292
|
-
|
|
350
|
+
{confirmInfo.text}
|
|
293
351
|
</Modal>
|
|
294
352
|
);
|
|
295
353
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input group with a title and space for buttons
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Import React
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
/*------------------------------------------------------------------------*/
|
|
10
|
+
/* Types */
|
|
11
|
+
/*------------------------------------------------------------------------*/
|
|
12
|
+
|
|
13
|
+
// Props definition
|
|
14
|
+
type Props = {
|
|
15
|
+
// Label text
|
|
16
|
+
label: string,
|
|
17
|
+
// Minimum width of the label (css units)
|
|
18
|
+
minLabelWidth?: string,
|
|
19
|
+
// Buttons
|
|
20
|
+
children: React.ReactNode,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/*------------------------------------------------------------------------*/
|
|
24
|
+
/* Component */
|
|
25
|
+
/*------------------------------------------------------------------------*/
|
|
26
|
+
|
|
27
|
+
const ButtonInputGroup: React.FC<Props> = (props) => {
|
|
28
|
+
/*------------------------------------------------------------------------*/
|
|
29
|
+
/* Setup */
|
|
30
|
+
/*------------------------------------------------------------------------*/
|
|
31
|
+
|
|
32
|
+
/* -------------- Props ------------- */
|
|
33
|
+
|
|
34
|
+
// Destructure all props
|
|
35
|
+
const {
|
|
36
|
+
label,
|
|
37
|
+
minLabelWidth,
|
|
38
|
+
children,
|
|
39
|
+
} = props;
|
|
40
|
+
|
|
41
|
+
/*------------------------------------------------------------------------*/
|
|
42
|
+
/* Render */
|
|
43
|
+
/*------------------------------------------------------------------------*/
|
|
44
|
+
|
|
45
|
+
/*----------------------------------------*/
|
|
46
|
+
/* Main UI */
|
|
47
|
+
/*----------------------------------------*/
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div className="input-group">
|
|
51
|
+
<div className="input-group-prepend d-flex w-100">
|
|
52
|
+
{/* Label */}
|
|
53
|
+
<span
|
|
54
|
+
className="input-group-text"
|
|
55
|
+
style={{
|
|
56
|
+
minWidth: (
|
|
57
|
+
minLabelWidth
|
|
58
|
+
?? undefined
|
|
59
|
+
),
|
|
60
|
+
borderTopRightRadius: 0,
|
|
61
|
+
borderBottomRightRadius: 0,
|
|
62
|
+
}}
|
|
63
|
+
>
|
|
64
|
+
{label}
|
|
65
|
+
</span>
|
|
66
|
+
|
|
67
|
+
{/* Contents */}
|
|
68
|
+
<span
|
|
69
|
+
className="input-group-text flex-grow-1 rounded-right"
|
|
70
|
+
style={{
|
|
71
|
+
backgroundColor: 'white',
|
|
72
|
+
borderTopLeftRadius: 0,
|
|
73
|
+
borderBottomLeftRadius: 0,
|
|
74
|
+
borderLeftWidth: 0,
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
{children}
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/*------------------------------------------------------------------------*/
|
|
85
|
+
/* Wrap Up */
|
|
86
|
+
/*------------------------------------------------------------------------*/
|
|
87
|
+
|
|
88
|
+
// Export component
|
|
89
|
+
export default ButtonInputGroup;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A checkbox button
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Import React
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
// Import FontAwesome Icons
|
|
10
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
11
|
+
import { faCheckSquare } from '@fortawesome/free-solid-svg-icons';
|
|
12
|
+
import { faSquare } from '@fortawesome/free-regular-svg-icons';
|
|
13
|
+
|
|
14
|
+
// Import shared types
|
|
15
|
+
import Variant from '../types/Variant';
|
|
16
|
+
|
|
17
|
+
/*------------------------------------------------------------------------*/
|
|
18
|
+
/* Types */
|
|
19
|
+
/*------------------------------------------------------------------------*/
|
|
20
|
+
|
|
21
|
+
type Props = {
|
|
22
|
+
// Text of the button
|
|
23
|
+
text: string,
|
|
24
|
+
// Handler for when this item is toggled
|
|
25
|
+
onChanged: (checked: boolean) => void,
|
|
26
|
+
// Aria label
|
|
27
|
+
ariaLabel: string,
|
|
28
|
+
// Button title
|
|
29
|
+
title?: string,
|
|
30
|
+
// True if checked
|
|
31
|
+
checked?: boolean,
|
|
32
|
+
// The id of the button
|
|
33
|
+
id?: string,
|
|
34
|
+
// If true, no margin on right
|
|
35
|
+
noMarginOnRight?: boolean,
|
|
36
|
+
// Variant for when checkbox is checked
|
|
37
|
+
checkedVariant?: Variant,
|
|
38
|
+
// Variant for when checkbox is not checked
|
|
39
|
+
uncheckedVariant?: Variant,
|
|
40
|
+
// True if using a small button
|
|
41
|
+
small?: boolean,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/*------------------------------------------------------------------------*/
|
|
45
|
+
/* Component */
|
|
46
|
+
/*------------------------------------------------------------------------*/
|
|
47
|
+
|
|
48
|
+
const CheckboxButton: React.FC<Props> = (props) => {
|
|
49
|
+
/*------------------------------------------------------------------------*/
|
|
50
|
+
/* Setup */
|
|
51
|
+
/*------------------------------------------------------------------------*/
|
|
52
|
+
|
|
53
|
+
/* -------------- Props ------------- */
|
|
54
|
+
|
|
55
|
+
const {
|
|
56
|
+
text,
|
|
57
|
+
onChanged,
|
|
58
|
+
ariaLabel,
|
|
59
|
+
title,
|
|
60
|
+
checked,
|
|
61
|
+
id,
|
|
62
|
+
noMarginOnRight,
|
|
63
|
+
checkedVariant = Variant.Secondary,
|
|
64
|
+
uncheckedVariant = Variant.Light,
|
|
65
|
+
small,
|
|
66
|
+
} = props;
|
|
67
|
+
|
|
68
|
+
/*------------------------------------------------------------------------*/
|
|
69
|
+
/* Render */
|
|
70
|
+
/*------------------------------------------------------------------------*/
|
|
71
|
+
|
|
72
|
+
/*----------------------------------------*/
|
|
73
|
+
/* Main UI */
|
|
74
|
+
/*----------------------------------------*/
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<button
|
|
78
|
+
type="button"
|
|
79
|
+
id={id}
|
|
80
|
+
title={title}
|
|
81
|
+
className={`btn btn-${checked ? checkedVariant : uncheckedVariant}${checked ? ' selected' : ''}${small ? ' btn-sm' : ''} m-0${noMarginOnRight ? '' : ' me-1'}`}
|
|
82
|
+
aria-label={`${ariaLabel}${checked ? ': currently checked' : ''}`}
|
|
83
|
+
onClick={() => {
|
|
84
|
+
onChanged(!checked);
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
<FontAwesomeIcon
|
|
88
|
+
icon={checked ? faCheckSquare : faSquare}
|
|
89
|
+
className="me-1"
|
|
90
|
+
/>
|
|
91
|
+
{text}
|
|
92
|
+
</button>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/*------------------------------------------------------------------------*/
|
|
97
|
+
/* Wrap up */
|
|
98
|
+
/*------------------------------------------------------------------------*/
|
|
99
|
+
|
|
100
|
+
export default CheckboxButton;
|
|
@@ -63,9 +63,9 @@ const ErrorBox: React.FC<Props> = (props) => {
|
|
|
63
63
|
<span
|
|
64
64
|
style={{
|
|
65
65
|
backgroundColor: 'white',
|
|
66
|
-
borderRadius: '
|
|
67
|
-
paddingLeft: '
|
|
68
|
-
paddingRight: '
|
|
66
|
+
borderRadius: '0.3rem',
|
|
67
|
+
paddingLeft: '0.2rem',
|
|
68
|
+
paddingRight: '0.2rem',
|
|
69
69
|
color: '#DC4150',
|
|
70
70
|
fontVariant: 'small-caps',
|
|
71
71
|
fontSize: '80%',
|
|
@@ -85,7 +85,7 @@ const ErrorBox: React.FC<Props> = (props) => {
|
|
|
85
85
|
<div
|
|
86
86
|
className="alert alert-danger text-center"
|
|
87
87
|
style={{
|
|
88
|
-
maxWidth: '
|
|
88
|
+
maxWidth: '40rem',
|
|
89
89
|
margin: 'auto',
|
|
90
90
|
}}
|
|
91
91
|
>
|
|
@@ -20,10 +20,10 @@ const style = `
|
|
|
20
20
|
.LoadingSpinner-blip-2,
|
|
21
21
|
.LoadingSpinner-blip-3,
|
|
22
22
|
.LoadingSpinner-blip-4 {
|
|
23
|
-
font-size:
|
|
23
|
+
font-size: 1.8rem;
|
|
24
24
|
opacity: 0.6;
|
|
25
|
-
margin-top:
|
|
26
|
-
margin-bottom:
|
|
25
|
+
margin-top: 1rem;
|
|
26
|
+
margin-bottom: 1rem;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/* First Blip */
|