@teselagen/ui 0.0.9 → 0.0.12
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/README.md +7 -0
- package/cypress.config.ts +6 -0
- package/index.html +12 -0
- package/package.json +2 -2
- package/project.json +74 -0
- package/src/AdvancedOptions.js +33 -0
- package/src/AdvancedOptions.spec.js +24 -0
- package/src/AssignDefaultsModeContext.js +21 -0
- package/src/AsyncValidateFieldSpinner/index.js +12 -0
- package/src/BlueprintError/index.js +14 -0
- package/src/BounceLoader/index.js +16 -0
- package/src/BounceLoader/style.css +45 -0
- package/src/CollapsibleCard/index.js +92 -0
- package/src/CollapsibleCard/style.css +21 -0
- package/src/DNALoader/index.js +20 -0
- package/src/DNALoader/style.css +251 -0
- package/src/DataTable/CellDragHandle.js +130 -0
- package/src/DataTable/DisabledLoadingComponent.js +15 -0
- package/src/DataTable/DisplayOptions.js +218 -0
- package/src/DataTable/FilterAndSortMenu.js +397 -0
- package/src/DataTable/PagingTool.js +232 -0
- package/src/DataTable/SearchBar.js +57 -0
- package/src/DataTable/SortableColumns.js +53 -0
- package/src/DataTable/TableFormTrackerContext.js +10 -0
- package/src/DataTable/dataTableEnhancer.js +291 -0
- package/src/DataTable/defaultFormatters.js +32 -0
- package/src/DataTable/defaultProps.js +45 -0
- package/src/DataTable/defaultValidators.js +40 -0
- package/src/DataTable/editCellHelper.js +44 -0
- package/src/DataTable/getCellVal.js +20 -0
- package/src/DataTable/getVals.js +8 -0
- package/src/DataTable/index.js +3537 -0
- package/src/DataTable/isTruthy.js +12 -0
- package/src/DataTable/isValueEmpty.js +3 -0
- package/src/DataTable/style.css +600 -0
- package/src/DataTable/utils/computePresets.js +42 -0
- package/src/DataTable/utils/convertSchema.js +69 -0
- package/src/DataTable/utils/getIdOrCodeOrIndex.js +9 -0
- package/src/DataTable/utils/getTableConfigFromStorage.js +5 -0
- package/src/DataTable/utils/queryParams.js +1032 -0
- package/src/DataTable/utils/rowClick.js +156 -0
- package/src/DataTable/utils/selection.js +8 -0
- package/src/DataTable/utils/withSelectedEntities.js +65 -0
- package/src/DataTable/utils/withTableParams.js +328 -0
- package/src/DataTable/validateTableWideErrors.js +135 -0
- package/src/DataTable/viewColumn.js +37 -0
- package/src/DialogFooter/index.js +79 -0
- package/src/DialogFooter/style.css +9 -0
- package/src/DropdownButton.js +36 -0
- package/src/FillWindow.css +6 -0
- package/src/FillWindow.js +69 -0
- package/src/FormComponents/Uploader.js +1197 -0
- package/src/FormComponents/getNewName.js +31 -0
- package/src/FormComponents/index.js +1384 -0
- package/src/FormComponents/itemUpload.js +84 -0
- package/src/FormComponents/sortify.js +73 -0
- package/src/FormComponents/style.css +247 -0
- package/src/FormComponents/tryToMatchSchemas.js +222 -0
- package/src/FormComponents/utils.js +6 -0
- package/src/HotkeysDialog/index.js +79 -0
- package/src/HotkeysDialog/style.css +54 -0
- package/src/InfoHelper/index.js +83 -0
- package/src/InfoHelper/style.css +7 -0
- package/src/IntentText/index.js +18 -0
- package/src/Loading/index.js +74 -0
- package/src/Loading/style.css +4 -0
- package/src/MatchHeaders.js +223 -0
- package/src/MenuBar/index.js +416 -0
- package/src/MenuBar/style.css +45 -0
- package/src/PromptUnsavedChanges/index.js +40 -0
- package/src/ResizableDraggableDialog/index.js +138 -0
- package/src/ResizableDraggableDialog/style.css +42 -0
- package/src/ScrollToTop/index.js +72 -0
- package/src/SimpleStepViz.js +26 -0
- package/src/TgSelect/index.js +465 -0
- package/src/TgSelect/style.css +34 -0
- package/src/TgSuggest/index.js +121 -0
- package/src/Timeline/TimelineEvent.js +31 -0
- package/src/Timeline/index.js +22 -0
- package/src/Timeline/style.css +29 -0
- package/src/UploadCsvWizard.css +4 -0
- package/src/UploadCsvWizard.js +731 -0
- package/src/autoTooltip.js +89 -0
- package/src/constants.js +1 -0
- package/src/customIcons.js +361 -0
- package/src/enhancers/withDialog/index.js +196 -0
- package/src/enhancers/withDialog/tg_modalState.js +46 -0
- package/src/enhancers/withField.js +20 -0
- package/src/enhancers/withFields.js +11 -0
- package/src/enhancers/withLocalStorage.js +11 -0
- package/src/index.js +76 -0
- package/src/rerenderOnWindowResize.js +27 -0
- package/src/showAppSpinner.js +12 -0
- package/src/showConfirmationDialog/index.js +116 -0
- package/src/showDialogOnDocBody.js +37 -0
- package/src/style.css +214 -0
- package/src/toastr.js +92 -0
- package/src/typeToCommonType.js +6 -0
- package/src/useDialog.js +64 -0
- package/src/utils/S3Download.js +14 -0
- package/src/utils/adHoc.js +10 -0
- package/src/utils/basicHandleActionsWithFullState.js +14 -0
- package/src/utils/combineReducersWithFullState.js +14 -0
- package/src/utils/commandControls.js +83 -0
- package/src/utils/commandUtils.js +112 -0
- package/src/utils/determineBlackOrWhiteTextColor.js +4 -0
- package/src/utils/getDayjsFormatter.js +35 -0
- package/src/utils/getTextFromEl.js +28 -0
- package/src/utils/handlerHelpers.js +30 -0
- package/src/utils/hotkeyUtils.js +129 -0
- package/src/utils/menuUtils.js +402 -0
- package/src/utils/popoverOverflowModifiers.js +11 -0
- package/src/utils/pureNoFunc.js +31 -0
- package/src/utils/renderOnDoc.js +29 -0
- package/src/utils/showProgressToast.js +22 -0
- package/src/utils/tagUtils.js +45 -0
- package/src/utils/tgFormValues.js +32 -0
- package/src/utils/withSelectTableRecords.js +38 -0
- package/src/utils/withStore.js +10 -0
- package/src/wrapDialog.js +112 -0
- package/tsconfig.json +4 -0
- package/vite.config.ts +7 -0
- package/index.js +0 -80652
- package/index.mjs +0 -80636
- package/index.umd.js +0 -80649
- package/style.css +0 -10421
|
@@ -0,0 +1,1384 @@
|
|
|
1
|
+
import classNames from "classnames";
|
|
2
|
+
import { SketchPicker } from "react-color";
|
|
3
|
+
import { isNumber, noop, kebabCase, isPlainObject, isEqual } from "lodash";
|
|
4
|
+
import mathExpressionEvaluator from "math-expression-evaluator";
|
|
5
|
+
import React, { useContext, useState } from "react";
|
|
6
|
+
import { Field, change } from "redux-form";
|
|
7
|
+
|
|
8
|
+
import "./style.css";
|
|
9
|
+
import {
|
|
10
|
+
InputGroup,
|
|
11
|
+
NumericInput,
|
|
12
|
+
Intent,
|
|
13
|
+
RadioGroup,
|
|
14
|
+
Checkbox,
|
|
15
|
+
EditableText,
|
|
16
|
+
Tooltip,
|
|
17
|
+
Position,
|
|
18
|
+
Switch,
|
|
19
|
+
Classes,
|
|
20
|
+
FormGroup,
|
|
21
|
+
Button,
|
|
22
|
+
TextArea,
|
|
23
|
+
Popover,
|
|
24
|
+
} from "@blueprintjs/core";
|
|
25
|
+
|
|
26
|
+
import { DateInput, DateRangeInput } from "@blueprintjs/datetime";
|
|
27
|
+
import useDeepCompareEffect from "use-deep-compare-effect";
|
|
28
|
+
import { difference } from "lodash";
|
|
29
|
+
import { set } from "lodash";
|
|
30
|
+
import TgSelect from "../TgSelect";
|
|
31
|
+
import TgSuggest from "../TgSuggest";
|
|
32
|
+
import InfoHelper from "../InfoHelper";
|
|
33
|
+
import getDayjsFormatter from "../utils/getDayjsFormatter";
|
|
34
|
+
import AsyncValidateFieldSpinner from "../AsyncValidateFieldSpinner";
|
|
35
|
+
import {
|
|
36
|
+
AssignDefaultsModeContext,
|
|
37
|
+
WorkflowDefaultParamsContext,
|
|
38
|
+
workflowDefaultParamsObj,
|
|
39
|
+
} from "../AssignDefaultsModeContext";
|
|
40
|
+
import popoverOverflowModifiers from "../utils/popoverOverflowModifiers";
|
|
41
|
+
import Uploader from "./Uploader";
|
|
42
|
+
import sortify from "./sortify";
|
|
43
|
+
import { fieldRequired } from "./utils";
|
|
44
|
+
|
|
45
|
+
export { fieldRequired };
|
|
46
|
+
|
|
47
|
+
function getIntent({
|
|
48
|
+
showErrorIfUntouched,
|
|
49
|
+
meta: { touched, error, warning },
|
|
50
|
+
}) {
|
|
51
|
+
const hasError = (touched || showErrorIfUntouched) && error;
|
|
52
|
+
const hasWarning = (touched || showErrorIfUntouched) && warning;
|
|
53
|
+
if (hasError) {
|
|
54
|
+
return Intent.DANGER;
|
|
55
|
+
} else if (hasWarning) {
|
|
56
|
+
return Intent.WARNING;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getIntentClass(...args) {
|
|
61
|
+
const intent = getIntent(...args);
|
|
62
|
+
if (intent === Intent.DANGER) {
|
|
63
|
+
return Classes.INTENT_DANGER;
|
|
64
|
+
} else if (intent === Intent.WARNING) {
|
|
65
|
+
return Classes.INTENT_WARNING;
|
|
66
|
+
} else {
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function removeUnwantedProps(props) {
|
|
72
|
+
const cleanedProps = { ...props };
|
|
73
|
+
delete cleanedProps.className;
|
|
74
|
+
delete cleanedProps.units;
|
|
75
|
+
delete cleanedProps.inlineLabel;
|
|
76
|
+
delete cleanedProps.isLabelTooltip;
|
|
77
|
+
delete cleanedProps.showErrorIfUntouched;
|
|
78
|
+
delete cleanedProps.onChange;
|
|
79
|
+
delete cleanedProps.containerStyle;
|
|
80
|
+
delete cleanedProps.onFieldSubmit;
|
|
81
|
+
delete cleanedProps.onBlur;
|
|
82
|
+
delete cleanedProps.intent;
|
|
83
|
+
delete cleanedProps.intentClass;
|
|
84
|
+
delete cleanedProps.meta;
|
|
85
|
+
delete cleanedProps.defaultValue;
|
|
86
|
+
delete cleanedProps.enableReinitialize;
|
|
87
|
+
delete cleanedProps.tabIndex;
|
|
88
|
+
delete cleanedProps.secondaryLabel;
|
|
89
|
+
delete cleanedProps.tooltipError;
|
|
90
|
+
delete cleanedProps.tooltipInfo;
|
|
91
|
+
delete cleanedProps.tooltipProps;
|
|
92
|
+
// delete cleanedProps.asyncValidate;
|
|
93
|
+
// delete cleanedProps.asyncValidating;
|
|
94
|
+
// delete cleanedProps.validateOnChange;
|
|
95
|
+
delete cleanedProps.hasCustomError;
|
|
96
|
+
if (cleanedProps.inputClassName) {
|
|
97
|
+
cleanedProps.className = cleanedProps.inputClassName;
|
|
98
|
+
delete cleanedProps.inputClassName;
|
|
99
|
+
}
|
|
100
|
+
return cleanedProps;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function LabelWithTooltipInfo({ label, tooltipInfo, labelStyle }) {
|
|
104
|
+
return tooltipInfo ? (
|
|
105
|
+
<div style={{ display: "flex", alignItems: "center", ...labelStyle }}>
|
|
106
|
+
{label}{" "}
|
|
107
|
+
<InfoHelper
|
|
108
|
+
style={{ marginLeft: "5px", marginTop: "-6px" }}
|
|
109
|
+
size={12}
|
|
110
|
+
content={tooltipInfo}
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
) : (
|
|
114
|
+
label || null
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
class AbstractInput extends React.Component {
|
|
119
|
+
componentDidMount() {
|
|
120
|
+
const {
|
|
121
|
+
defaultValue,
|
|
122
|
+
enableReinitialize,
|
|
123
|
+
input: { value },
|
|
124
|
+
} = this.props;
|
|
125
|
+
if (
|
|
126
|
+
((value !== false && !value) || enableReinitialize) &&
|
|
127
|
+
defaultValue !== undefined
|
|
128
|
+
) {
|
|
129
|
+
this.updateDefaultValue();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
componentDidUpdate(oldProps) {
|
|
134
|
+
const {
|
|
135
|
+
defaultValue: oldDefaultValue,
|
|
136
|
+
defaultValCount: oldDefaultValCount,
|
|
137
|
+
} = oldProps;
|
|
138
|
+
const {
|
|
139
|
+
defaultValue,
|
|
140
|
+
defaultValCount,
|
|
141
|
+
enableReinitialize,
|
|
142
|
+
input: { value },
|
|
143
|
+
} = this.props;
|
|
144
|
+
|
|
145
|
+
if (
|
|
146
|
+
((value !== false && !value) ||
|
|
147
|
+
enableReinitialize ||
|
|
148
|
+
defaultValCount !== oldDefaultValCount) &&
|
|
149
|
+
!isEqual(defaultValue, oldDefaultValue)
|
|
150
|
+
) {
|
|
151
|
+
this.updateDefaultValue();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
updateDefaultValue = () => {
|
|
156
|
+
const {
|
|
157
|
+
defaultValue,
|
|
158
|
+
input: { name },
|
|
159
|
+
meta: { dispatch, form },
|
|
160
|
+
onDefaultValChanged,
|
|
161
|
+
onFieldSubmit,
|
|
162
|
+
} = this.props;
|
|
163
|
+
dispatch(change(form, name, defaultValue));
|
|
164
|
+
onDefaultValChanged &&
|
|
165
|
+
onDefaultValChanged(defaultValue, name, form, this.props);
|
|
166
|
+
onFieldSubmit && onFieldSubmit(defaultValue);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
render() {
|
|
170
|
+
const {
|
|
171
|
+
children,
|
|
172
|
+
tooltipProps,
|
|
173
|
+
tooltipError,
|
|
174
|
+
disabled,
|
|
175
|
+
intent,
|
|
176
|
+
tooltipInfo,
|
|
177
|
+
label,
|
|
178
|
+
inlineLabel,
|
|
179
|
+
isLabelTooltip,
|
|
180
|
+
secondaryLabel,
|
|
181
|
+
className,
|
|
182
|
+
showErrorIfUntouched,
|
|
183
|
+
asyncValidating,
|
|
184
|
+
meta,
|
|
185
|
+
containerStyle,
|
|
186
|
+
leftEl,
|
|
187
|
+
rightEl,
|
|
188
|
+
labelStyle,
|
|
189
|
+
noOuterLabel,
|
|
190
|
+
fileLimit,
|
|
191
|
+
noMarginBottom,
|
|
192
|
+
assignDefaultButton,
|
|
193
|
+
showGenerateDefaultDot,
|
|
194
|
+
setAssignDefaultsMode,
|
|
195
|
+
startAssigningDefault,
|
|
196
|
+
input,
|
|
197
|
+
noFillField,
|
|
198
|
+
isRequired,
|
|
199
|
+
isLoadingDefaultValue,
|
|
200
|
+
} = this.props;
|
|
201
|
+
const { touched, error, warning } = meta;
|
|
202
|
+
|
|
203
|
+
// if our custom field level validation is happening then we don't want to show the error visually
|
|
204
|
+
const showError =
|
|
205
|
+
(touched || showErrorIfUntouched) && error && !asyncValidating;
|
|
206
|
+
const showWarning = (touched || showErrorIfUntouched) && warning;
|
|
207
|
+
let componentToWrap =
|
|
208
|
+
isLabelTooltip || tooltipError ? (
|
|
209
|
+
<Tooltip
|
|
210
|
+
disabled={isLabelTooltip ? false : !showError}
|
|
211
|
+
intent={isLabelTooltip ? "none" : error ? "danger" : "warning"}
|
|
212
|
+
content={isLabelTooltip ? label : error || warning}
|
|
213
|
+
position={Position.TOP}
|
|
214
|
+
modifiers={popoverOverflowModifiers}
|
|
215
|
+
{...tooltipProps}
|
|
216
|
+
>
|
|
217
|
+
{children}
|
|
218
|
+
</Tooltip>
|
|
219
|
+
) : (
|
|
220
|
+
children
|
|
221
|
+
);
|
|
222
|
+
const testClassName = "tg-test-" + kebabCase(input.name);
|
|
223
|
+
if (noFillField) {
|
|
224
|
+
componentToWrap = (
|
|
225
|
+
<div className="tg-no-fill-field">{componentToWrap}</div>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let helperText;
|
|
230
|
+
if (!tooltipError) {
|
|
231
|
+
if (showError) {
|
|
232
|
+
helperText = error;
|
|
233
|
+
} else if (showWarning) {
|
|
234
|
+
helperText = warning;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// if in a cypress test show message so that inputs will not be interactable
|
|
239
|
+
if (window.Cypress && isLoadingDefaultValue) {
|
|
240
|
+
return "Loading default value...";
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
let labelInfo = secondaryLabel;
|
|
244
|
+
|
|
245
|
+
const hasOuterLabel = !noOuterLabel && !isLabelTooltip;
|
|
246
|
+
function getFileLimitInfo() {
|
|
247
|
+
if (!fileLimit) return "";
|
|
248
|
+
return `max ${fileLimit} file${fileLimit === 1 ? "" : "s"}`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (isRequired && hasOuterLabel && label && !labelInfo) {
|
|
252
|
+
labelInfo = `(required${fileLimit ? `, ${getFileLimitInfo()}` : ""})`;
|
|
253
|
+
} else if (!labelInfo && fileLimit) {
|
|
254
|
+
labelInfo = `(${getFileLimitInfo()})`;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<FormGroup
|
|
259
|
+
className={classNames(className, testClassName, {
|
|
260
|
+
"tg-flex-form-content": leftEl || rightEl,
|
|
261
|
+
"tg-tooltipError": tooltipError,
|
|
262
|
+
"tg-has-error": showError && error,
|
|
263
|
+
})}
|
|
264
|
+
disabled={disabled}
|
|
265
|
+
helperText={helperText}
|
|
266
|
+
intent={intent}
|
|
267
|
+
label={
|
|
268
|
+
hasOuterLabel && (
|
|
269
|
+
<LabelWithTooltipInfo
|
|
270
|
+
labelStyle={labelStyle}
|
|
271
|
+
label={label}
|
|
272
|
+
tooltipInfo={tooltipInfo}
|
|
273
|
+
/>
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
inline={inlineLabel}
|
|
277
|
+
labelInfo={labelInfo}
|
|
278
|
+
style={{
|
|
279
|
+
...(noMarginBottom && { marginBottom: 0 }),
|
|
280
|
+
...containerStyle,
|
|
281
|
+
}}
|
|
282
|
+
>
|
|
283
|
+
{showGenerateDefaultDot && (
|
|
284
|
+
<div
|
|
285
|
+
style={{ zIndex: 10, position: "relative", height: 0, width: 0 }}
|
|
286
|
+
>
|
|
287
|
+
<div style={{ position: "absolute", left: "0px", top: "0px" }}>
|
|
288
|
+
<Tooltip
|
|
289
|
+
modifiers={popoverOverflowModifiers}
|
|
290
|
+
content="Allows a Default to be Set. Click to Enter Set Default Mode (or press Shift+D when outside the input field)"
|
|
291
|
+
>
|
|
292
|
+
<div
|
|
293
|
+
onClick={() => {
|
|
294
|
+
setAssignDefaultsMode(true);
|
|
295
|
+
startAssigningDefault();
|
|
296
|
+
}}
|
|
297
|
+
className="generateDefaultDot"
|
|
298
|
+
></div>
|
|
299
|
+
</Tooltip>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
)}
|
|
303
|
+
{assignDefaultButton}
|
|
304
|
+
{leftEl} {componentToWrap} {rightEl}
|
|
305
|
+
</FormGroup>
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export const renderBlueprintDateInput = (props) => {
|
|
311
|
+
const { input, intent, onFieldSubmit, inputProps, ...rest } = props;
|
|
312
|
+
return (
|
|
313
|
+
<DateInput
|
|
314
|
+
{...getDayjsFormatter("L")}
|
|
315
|
+
{...removeUnwantedProps(rest)}
|
|
316
|
+
intent={intent}
|
|
317
|
+
inputProps={inputProps}
|
|
318
|
+
{...input}
|
|
319
|
+
value={input.value ? new Date(input.value) : undefined}
|
|
320
|
+
onChange={function (selectedDate) {
|
|
321
|
+
input.onChange(selectedDate);
|
|
322
|
+
onFieldSubmit(selectedDate);
|
|
323
|
+
}}
|
|
324
|
+
/>
|
|
325
|
+
);
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
export const renderBlueprintDateRangeInput = (props) => {
|
|
329
|
+
const { input, intent, onFieldSubmit, inputProps, ...rest } = props;
|
|
330
|
+
|
|
331
|
+
return (
|
|
332
|
+
<DateRangeInput
|
|
333
|
+
{...getDayjsFormatter("L")}
|
|
334
|
+
{...removeUnwantedProps(rest)}
|
|
335
|
+
intent={intent}
|
|
336
|
+
inputProps={inputProps}
|
|
337
|
+
{...input}
|
|
338
|
+
value={
|
|
339
|
+
input.value
|
|
340
|
+
? [new Date(input.value[0]), new Date(input.value[1])]
|
|
341
|
+
: undefined
|
|
342
|
+
}
|
|
343
|
+
onChange={function (selectedDate) {
|
|
344
|
+
input.onChange(selectedDate);
|
|
345
|
+
onFieldSubmit(selectedDate);
|
|
346
|
+
}}
|
|
347
|
+
/>
|
|
348
|
+
);
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
export const RenderBlueprintInput = (props) => {
|
|
352
|
+
const {
|
|
353
|
+
input,
|
|
354
|
+
// meta = {},
|
|
355
|
+
intent,
|
|
356
|
+
onFieldSubmit,
|
|
357
|
+
onKeyDown = noop,
|
|
358
|
+
asyncValidating,
|
|
359
|
+
rightElement,
|
|
360
|
+
clickToEdit,
|
|
361
|
+
...rest
|
|
362
|
+
} = props;
|
|
363
|
+
const [isOpen, setOpen] = useState(false);
|
|
364
|
+
const [value, setVal] = useState(null);
|
|
365
|
+
const toSpread = {};
|
|
366
|
+
if (clickToEdit) {
|
|
367
|
+
const isDisabled = clickToEdit && !isOpen;
|
|
368
|
+
toSpread.disabled = rest.disabled || isDisabled;
|
|
369
|
+
}
|
|
370
|
+
const stopEdit = () => {
|
|
371
|
+
setOpen(false);
|
|
372
|
+
setVal(null);
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
const inner =
|
|
376
|
+
clickToEdit && !isOpen ? (
|
|
377
|
+
<div>{input.value}</div>
|
|
378
|
+
) : (
|
|
379
|
+
<InputGroup
|
|
380
|
+
rightElement={
|
|
381
|
+
asyncValidating ? (
|
|
382
|
+
<AsyncValidateFieldSpinner validating />
|
|
383
|
+
) : (
|
|
384
|
+
rightElement
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
{...removeUnwantedProps(rest)}
|
|
388
|
+
intent={intent}
|
|
389
|
+
{...input}
|
|
390
|
+
{...toSpread}
|
|
391
|
+
{...(clickToEdit
|
|
392
|
+
? {
|
|
393
|
+
disabled: rest.disabled,
|
|
394
|
+
onChange: (e) => {
|
|
395
|
+
setVal(e.target.value);
|
|
396
|
+
},
|
|
397
|
+
...(value === null ? {} : { value }),
|
|
398
|
+
onKeyDown: (...args) => {
|
|
399
|
+
onKeyDown(...args);
|
|
400
|
+
const e = args[0];
|
|
401
|
+
if (e.key === "Enter") {
|
|
402
|
+
input.onChange(value === null ? input.value : value);
|
|
403
|
+
onFieldSubmit(e.target.value, { enter: true }, e);
|
|
404
|
+
stopEdit();
|
|
405
|
+
}
|
|
406
|
+
return true;
|
|
407
|
+
},
|
|
408
|
+
}
|
|
409
|
+
: {
|
|
410
|
+
onKeyDown: function (...args) {
|
|
411
|
+
onKeyDown(...args);
|
|
412
|
+
const e = args[0];
|
|
413
|
+
if (e.key === "Enter") {
|
|
414
|
+
onFieldSubmit(e.target.value, { enter: true }, e);
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
onBlur: function (e, val) {
|
|
418
|
+
if (rest.readOnly) return;
|
|
419
|
+
input.onBlur(e, val);
|
|
420
|
+
onFieldSubmit(
|
|
421
|
+
e.target ? e.target.value : val,
|
|
422
|
+
{ blur: true },
|
|
423
|
+
e
|
|
424
|
+
);
|
|
425
|
+
},
|
|
426
|
+
})}
|
|
427
|
+
/>
|
|
428
|
+
);
|
|
429
|
+
if (clickToEdit)
|
|
430
|
+
return (
|
|
431
|
+
<div style={{ display: "flex" }}>
|
|
432
|
+
{inner}
|
|
433
|
+
{clickToEdit &&
|
|
434
|
+
(isOpen ? (
|
|
435
|
+
<>
|
|
436
|
+
<Button
|
|
437
|
+
icon="small-cross"
|
|
438
|
+
onClick={stopEdit}
|
|
439
|
+
intent="danger"
|
|
440
|
+
></Button>
|
|
441
|
+
<Button
|
|
442
|
+
icon="small-tick"
|
|
443
|
+
onClick={() => {
|
|
444
|
+
input.onChange(value === null ? input.value : value);
|
|
445
|
+
onFieldSubmit(value === null ? input.value : value, {
|
|
446
|
+
cmdEnter: true,
|
|
447
|
+
});
|
|
448
|
+
stopEdit();
|
|
449
|
+
}}
|
|
450
|
+
intent="success"
|
|
451
|
+
></Button>{" "}
|
|
452
|
+
</>
|
|
453
|
+
) : (
|
|
454
|
+
<Button
|
|
455
|
+
minimal
|
|
456
|
+
onClick={() => {
|
|
457
|
+
setOpen(true);
|
|
458
|
+
}}
|
|
459
|
+
icon="edit"
|
|
460
|
+
></Button>
|
|
461
|
+
))}
|
|
462
|
+
</div>
|
|
463
|
+
);
|
|
464
|
+
return inner;
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
export const renderBlueprintCheckbox = (props) => {
|
|
468
|
+
const { input, label, tooltipInfo, beforeOnChange, onFieldSubmit, ...rest } =
|
|
469
|
+
props;
|
|
470
|
+
return (
|
|
471
|
+
<Checkbox
|
|
472
|
+
{...removeUnwantedProps(rest)}
|
|
473
|
+
{...input}
|
|
474
|
+
checked={input.value}
|
|
475
|
+
label={<LabelWithTooltipInfo label={label} tooltipInfo={tooltipInfo} />}
|
|
476
|
+
onChange={getCheckboxOrSwitchOnChange({
|
|
477
|
+
beforeOnChange,
|
|
478
|
+
input,
|
|
479
|
+
onFieldSubmit,
|
|
480
|
+
})}
|
|
481
|
+
/>
|
|
482
|
+
);
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
const getCheckboxOrSwitchOnChange = ({
|
|
486
|
+
beforeOnChange,
|
|
487
|
+
input,
|
|
488
|
+
onFieldSubmit,
|
|
489
|
+
}) =>
|
|
490
|
+
async function (e, val) {
|
|
491
|
+
const v = e.target ? e.target.checked : val;
|
|
492
|
+
if (beforeOnChange) {
|
|
493
|
+
const { stopEarly } = (await beforeOnChange(v, e)) || {};
|
|
494
|
+
if (stopEarly) return;
|
|
495
|
+
set(e, "target.checked", v);
|
|
496
|
+
}
|
|
497
|
+
input.onChange(e, val);
|
|
498
|
+
onFieldSubmit(v);
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
export const renderBlueprintSwitch = (props) => {
|
|
502
|
+
const { input, label, tooltipInfo, onFieldSubmit, beforeOnChange, ...rest } =
|
|
503
|
+
props;
|
|
504
|
+
|
|
505
|
+
return (
|
|
506
|
+
<Switch
|
|
507
|
+
{...removeUnwantedProps(rest)}
|
|
508
|
+
{...input}
|
|
509
|
+
checked={input.value}
|
|
510
|
+
label={<LabelWithTooltipInfo label={label} tooltipInfo={tooltipInfo} />}
|
|
511
|
+
onChange={getCheckboxOrSwitchOnChange({
|
|
512
|
+
beforeOnChange,
|
|
513
|
+
input,
|
|
514
|
+
onFieldSubmit,
|
|
515
|
+
})}
|
|
516
|
+
/>
|
|
517
|
+
);
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
export const renderFileUpload = (props) => {
|
|
521
|
+
const { input, onFieldSubmit, ...rest } = props;
|
|
522
|
+
return (
|
|
523
|
+
<Uploader
|
|
524
|
+
fileList={input.value}
|
|
525
|
+
onFieldSubmit={onFieldSubmit}
|
|
526
|
+
{...rest}
|
|
527
|
+
onChange={input.onChange}
|
|
528
|
+
/>
|
|
529
|
+
);
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
export class renderBlueprintTextarea extends React.Component {
|
|
533
|
+
state = {
|
|
534
|
+
value: null,
|
|
535
|
+
isOpen: false,
|
|
536
|
+
};
|
|
537
|
+
allowEdit = () => {
|
|
538
|
+
this.setState({ isOpen: true });
|
|
539
|
+
};
|
|
540
|
+
stopEdit = () => {
|
|
541
|
+
this.setState({ isOpen: false });
|
|
542
|
+
this.setState({ value: null });
|
|
543
|
+
};
|
|
544
|
+
updateVal = (e) => {
|
|
545
|
+
this.setState({ value: e.target.value });
|
|
546
|
+
};
|
|
547
|
+
handleValSubmit = () => {
|
|
548
|
+
this.props.input.onChange(
|
|
549
|
+
this.state.value === null ? this.props.input.value : this.state.value
|
|
550
|
+
);
|
|
551
|
+
this.props.onFieldSubmit(
|
|
552
|
+
this.state.value === null ? this.props.input.value : this.state.value,
|
|
553
|
+
{ cmdEnter: true }
|
|
554
|
+
);
|
|
555
|
+
this.stopEdit();
|
|
556
|
+
};
|
|
557
|
+
onKeyDown = (...args) => {
|
|
558
|
+
const e = args[0];
|
|
559
|
+
(this.props.onKeyDown || noop)(...args);
|
|
560
|
+
if (e.keyCode === 13 && (e.metaKey || e.ctrlKey)) {
|
|
561
|
+
this.props.onFieldSubmit(e.target.value, { cmdEnter: true }, e);
|
|
562
|
+
this.props.input.onChange(e);
|
|
563
|
+
this.stopEdit();
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
render() {
|
|
567
|
+
const {
|
|
568
|
+
input,
|
|
569
|
+
intentClass,
|
|
570
|
+
inputClassName,
|
|
571
|
+
onFieldSubmit,
|
|
572
|
+
clickToEdit,
|
|
573
|
+
onKeyDown,
|
|
574
|
+
disabled,
|
|
575
|
+
...rest
|
|
576
|
+
} = this.props;
|
|
577
|
+
if (clickToEdit) {
|
|
578
|
+
const isDisabled = clickToEdit && !this.state.isOpen;
|
|
579
|
+
|
|
580
|
+
return (
|
|
581
|
+
<React.Fragment>
|
|
582
|
+
<TextArea
|
|
583
|
+
{...removeUnwantedProps(rest)}
|
|
584
|
+
disabled={rest.disabled || isDisabled}
|
|
585
|
+
className={classNames(
|
|
586
|
+
intentClass,
|
|
587
|
+
inputClassName,
|
|
588
|
+
Classes.INPUT,
|
|
589
|
+
Classes.FILL
|
|
590
|
+
)}
|
|
591
|
+
value={this.state.value === null ? input.value : this.state.value}
|
|
592
|
+
onChange={this.updateVal}
|
|
593
|
+
onKeyDown={this.onKeyDown}
|
|
594
|
+
/>
|
|
595
|
+
{clickToEdit &&
|
|
596
|
+
!disabled &&
|
|
597
|
+
(this.state.isOpen ? (
|
|
598
|
+
//show okay/cancel buttons
|
|
599
|
+
<div>
|
|
600
|
+
<Button onClick={this.stopEdit} intent="danger">
|
|
601
|
+
Cancel
|
|
602
|
+
</Button>
|
|
603
|
+
<Button onClick={this.handleValSubmit} intent="success">
|
|
604
|
+
Ok
|
|
605
|
+
</Button>
|
|
606
|
+
</div>
|
|
607
|
+
) : (
|
|
608
|
+
//show click to edit button
|
|
609
|
+
<Button onClick={this.allowEdit}>Edit</Button>
|
|
610
|
+
))}
|
|
611
|
+
</React.Fragment>
|
|
612
|
+
);
|
|
613
|
+
} else {
|
|
614
|
+
return (
|
|
615
|
+
<TextArea
|
|
616
|
+
{...removeUnwantedProps(rest)}
|
|
617
|
+
disabled={disabled}
|
|
618
|
+
className={classNames(
|
|
619
|
+
intentClass,
|
|
620
|
+
inputClassName,
|
|
621
|
+
Classes.INPUT,
|
|
622
|
+
Classes.FILL
|
|
623
|
+
)}
|
|
624
|
+
{...input}
|
|
625
|
+
onBlur={function (e, val) {
|
|
626
|
+
if (rest.readOnly) return;
|
|
627
|
+
input.onBlur(e, val);
|
|
628
|
+
onFieldSubmit(e.target ? e.target.value : val, { blur: true }, e);
|
|
629
|
+
}}
|
|
630
|
+
onKeyDown={(...args) => {
|
|
631
|
+
const e = args[0];
|
|
632
|
+
(onKeyDown || noop)(...args);
|
|
633
|
+
if (e.keyCode === 13 && (e.metaKey || e.ctrlKey)) {
|
|
634
|
+
onFieldSubmit(e.target.value, { cmdEnter: true }, e);
|
|
635
|
+
}
|
|
636
|
+
}}
|
|
637
|
+
/>
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// class ClickToEditWrapper extends React.Component {
|
|
644
|
+
// state = { isEditing: false };
|
|
645
|
+
// render() {
|
|
646
|
+
// return <div />;
|
|
647
|
+
// }
|
|
648
|
+
// }
|
|
649
|
+
|
|
650
|
+
export const renderBlueprintEditableText = (props) => {
|
|
651
|
+
const { input, onFieldSubmit, ...rest } = props;
|
|
652
|
+
return (
|
|
653
|
+
<EditableText
|
|
654
|
+
{...removeUnwantedProps(rest)}
|
|
655
|
+
{...input}
|
|
656
|
+
onConfirm={function (value) {
|
|
657
|
+
input.onBlur && input.onBlur(value);
|
|
658
|
+
onFieldSubmit(value, { input, meta: rest.meta });
|
|
659
|
+
}}
|
|
660
|
+
/>
|
|
661
|
+
);
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
export const renderReactSelect = (props) => {
|
|
665
|
+
// spreading input not working, grab the values needed instead
|
|
666
|
+
const {
|
|
667
|
+
async,
|
|
668
|
+
input: { value, onChange },
|
|
669
|
+
hideValue,
|
|
670
|
+
intent,
|
|
671
|
+
options,
|
|
672
|
+
onFieldSubmit,
|
|
673
|
+
beforeOnChange,
|
|
674
|
+
...rest
|
|
675
|
+
} = props;
|
|
676
|
+
|
|
677
|
+
const optionsPassed = options;
|
|
678
|
+
|
|
679
|
+
const optsToUse = getOptions(optionsPassed);
|
|
680
|
+
let valueToUse;
|
|
681
|
+
|
|
682
|
+
if (!Array.isArray(value) && typeof value === "object") {
|
|
683
|
+
if (value.userCreated) {
|
|
684
|
+
valueToUse = {
|
|
685
|
+
label: value.value,
|
|
686
|
+
value,
|
|
687
|
+
};
|
|
688
|
+
} else {
|
|
689
|
+
valueToUse = optsToUse.find((obj) => {
|
|
690
|
+
return isEqual(obj.value, value);
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
} else if (Array.isArray(value)) {
|
|
694
|
+
valueToUse = value.map((val) => {
|
|
695
|
+
if (val.userCreated) {
|
|
696
|
+
return {
|
|
697
|
+
label: val.value,
|
|
698
|
+
value: val,
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
if (optsToUse) {
|
|
702
|
+
return optsToUse.find((obj) => {
|
|
703
|
+
return isEqual(obj.value, val);
|
|
704
|
+
});
|
|
705
|
+
} else {
|
|
706
|
+
return val;
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
} else {
|
|
710
|
+
valueToUse = value;
|
|
711
|
+
}
|
|
712
|
+
const propsToUse = {
|
|
713
|
+
...removeUnwantedProps(rest),
|
|
714
|
+
intent,
|
|
715
|
+
options: optsToUse,
|
|
716
|
+
value: valueToUse,
|
|
717
|
+
// closeOnSelect: !rest.multi,
|
|
718
|
+
async onChange(valOrVals, ...rest2) {
|
|
719
|
+
let valToPass;
|
|
720
|
+
if (Array.isArray(valOrVals)) {
|
|
721
|
+
valToPass = valOrVals.map(function (val) {
|
|
722
|
+
if (val.userCreated) {
|
|
723
|
+
return val;
|
|
724
|
+
}
|
|
725
|
+
return val.value;
|
|
726
|
+
});
|
|
727
|
+
} else if (valOrVals) {
|
|
728
|
+
if (valOrVals.userCreated) {
|
|
729
|
+
valToPass = valOrVals;
|
|
730
|
+
} else {
|
|
731
|
+
valToPass = valOrVals.value;
|
|
732
|
+
}
|
|
733
|
+
} else {
|
|
734
|
+
valToPass = "";
|
|
735
|
+
}
|
|
736
|
+
if (props.cancelSubmit && props.cancelSubmit(valToPass)) {
|
|
737
|
+
//allow the user to cancel the submit
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
if (beforeOnChange) {
|
|
741
|
+
const { stopEarly } = (await beforeOnChange(valToPass, ...rest2)) || {};
|
|
742
|
+
if (stopEarly) return;
|
|
743
|
+
}
|
|
744
|
+
onChange(valToPass, ...rest2);
|
|
745
|
+
if (!rest.submitOnBlur) onFieldSubmit(valToPass);
|
|
746
|
+
},
|
|
747
|
+
onBlur() {
|
|
748
|
+
const valToPass = Array.isArray(valueToUse)
|
|
749
|
+
? valueToUse
|
|
750
|
+
.filter((val) => !!val)
|
|
751
|
+
.map(function (val) {
|
|
752
|
+
return val.value;
|
|
753
|
+
})
|
|
754
|
+
: valueToUse;
|
|
755
|
+
if (props.cancelSubmit && props.cancelSubmit(valToPass)) {
|
|
756
|
+
return; //allow the user to cancel the submit
|
|
757
|
+
}
|
|
758
|
+
if (rest.submitOnBlur) {
|
|
759
|
+
onFieldSubmit(valToPass);
|
|
760
|
+
}
|
|
761
|
+
},
|
|
762
|
+
};
|
|
763
|
+
return <TgSelect {...propsToUse} />;
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
export const renderSuggest_old = (props) => {
|
|
767
|
+
return renderReactSelect({ ...props, asSuggest: true });
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
export const renderSuggest = (props) => {
|
|
771
|
+
const {
|
|
772
|
+
async,
|
|
773
|
+
input: { value, onChange },
|
|
774
|
+
hideValue,
|
|
775
|
+
intent,
|
|
776
|
+
options,
|
|
777
|
+
onFieldSubmit,
|
|
778
|
+
...rest
|
|
779
|
+
} = props;
|
|
780
|
+
|
|
781
|
+
const propsToUse = {
|
|
782
|
+
...removeUnwantedProps(rest),
|
|
783
|
+
intent,
|
|
784
|
+
options,
|
|
785
|
+
value,
|
|
786
|
+
onChange(val) {
|
|
787
|
+
onChange(val);
|
|
788
|
+
if (!rest.submitOnBlur) onFieldSubmit(val);
|
|
789
|
+
},
|
|
790
|
+
onBlur() {
|
|
791
|
+
if (rest.submitOnBlur) {
|
|
792
|
+
onFieldSubmit(value);
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
};
|
|
796
|
+
return <TgSuggest {...propsToUse}></TgSuggest>;
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
export const BPSelect = ({ value, onChange, ...rest }) => {
|
|
800
|
+
return renderSelect({ ...rest, input: { onChange, value } });
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
export const renderSelect = (props) => {
|
|
804
|
+
// spreading input not working, grab the values needed instead
|
|
805
|
+
const {
|
|
806
|
+
input: { value, onChange },
|
|
807
|
+
hideValue,
|
|
808
|
+
className,
|
|
809
|
+
placeholder,
|
|
810
|
+
onFieldSubmit,
|
|
811
|
+
options,
|
|
812
|
+
hidePlaceHolder,
|
|
813
|
+
minimal,
|
|
814
|
+
disabled,
|
|
815
|
+
...rest
|
|
816
|
+
} = props;
|
|
817
|
+
return (
|
|
818
|
+
<div
|
|
819
|
+
className={
|
|
820
|
+
`${minimal && Classes.MINIMAL} ` +
|
|
821
|
+
classNames(Classes.SELECT, Classes.FILL, className)
|
|
822
|
+
}
|
|
823
|
+
>
|
|
824
|
+
<select
|
|
825
|
+
{...removeUnwantedProps(rest)}
|
|
826
|
+
className={`${disabled && Classes.DISABLED} `}
|
|
827
|
+
value={
|
|
828
|
+
placeholder && value === ""
|
|
829
|
+
? "__placeholder__"
|
|
830
|
+
: typeof value !== "string"
|
|
831
|
+
? sortify(value) //deterministically sort and stringify the object/number coming in because select fields only support string values
|
|
832
|
+
: value
|
|
833
|
+
}
|
|
834
|
+
disabled={disabled}
|
|
835
|
+
{...(hideValue ? { value: "" } : {})}
|
|
836
|
+
onChange={function (e) {
|
|
837
|
+
let val = e.target.value;
|
|
838
|
+
try {
|
|
839
|
+
const maybeNewValue = JSON.parse(e.target.value); //try to json parse the string coming in
|
|
840
|
+
const hasMatchInOriginalOptions = options.find(
|
|
841
|
+
(opt) => opt === maybeNewValue || opt.value === maybeNewValue
|
|
842
|
+
);
|
|
843
|
+
if (hasMatchInOriginalOptions || isPlainObject(maybeNewValue)) {
|
|
844
|
+
val = maybeNewValue;
|
|
845
|
+
}
|
|
846
|
+
} catch (e) {
|
|
847
|
+
//empty
|
|
848
|
+
}
|
|
849
|
+
onChange(val);
|
|
850
|
+
onFieldSubmit && onFieldSubmit(val);
|
|
851
|
+
}}
|
|
852
|
+
>
|
|
853
|
+
{placeholder && (
|
|
854
|
+
<option value="__placeholder__" disabled hidden={hidePlaceHolder}>
|
|
855
|
+
{placeholder}
|
|
856
|
+
</option>
|
|
857
|
+
)}
|
|
858
|
+
{options.map(function (opt, index) {
|
|
859
|
+
let label, value;
|
|
860
|
+
if (typeof opt === "string") {
|
|
861
|
+
//support passing opts like: ['asdf','awfw']
|
|
862
|
+
label = opt;
|
|
863
|
+
value = opt;
|
|
864
|
+
} else if (isNumber(opt)) {
|
|
865
|
+
//support passing opts like: [1,2,3,4]
|
|
866
|
+
label = opt.toString();
|
|
867
|
+
value = opt;
|
|
868
|
+
} else if (Array.isArray(opt)) {
|
|
869
|
+
throw new Error(
|
|
870
|
+
"the option coming in should be an object, not an array!"
|
|
871
|
+
);
|
|
872
|
+
} else {
|
|
873
|
+
//support passing opts the normal way [{label: 'opt1', value: 'hey'}]
|
|
874
|
+
label = opt.label;
|
|
875
|
+
value = opt.value;
|
|
876
|
+
}
|
|
877
|
+
return (
|
|
878
|
+
<option
|
|
879
|
+
key={index}
|
|
880
|
+
value={
|
|
881
|
+
typeof value !== "string"
|
|
882
|
+
? sortify(value) //deterministically sort and stringify the object/number coming in because select fields only support string values
|
|
883
|
+
: value
|
|
884
|
+
}
|
|
885
|
+
>
|
|
886
|
+
{label}
|
|
887
|
+
</option>
|
|
888
|
+
);
|
|
889
|
+
})}
|
|
890
|
+
</select>
|
|
891
|
+
</div>
|
|
892
|
+
);
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
export const renderBlueprintNumericInput = (props) => {
|
|
896
|
+
const {
|
|
897
|
+
input,
|
|
898
|
+
hideValue,
|
|
899
|
+
intent,
|
|
900
|
+
inputClassName,
|
|
901
|
+
onFieldSubmit,
|
|
902
|
+
onAnyNumberChange,
|
|
903
|
+
...rest
|
|
904
|
+
} = props;
|
|
905
|
+
function handleBlurOrButtonClick(stringVal) {
|
|
906
|
+
if (rest.readOnly) return;
|
|
907
|
+
try {
|
|
908
|
+
const num = mathExpressionEvaluator.eval(stringVal);
|
|
909
|
+
input.onBlur(num);
|
|
910
|
+
onFieldSubmit(num);
|
|
911
|
+
} catch (e) {
|
|
912
|
+
console.error(
|
|
913
|
+
"TRC: Error occurring when setting evaluated numeric input field:",
|
|
914
|
+
e
|
|
915
|
+
);
|
|
916
|
+
input.onBlur("");
|
|
917
|
+
onFieldSubmit("");
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
return (
|
|
921
|
+
<NumericInput
|
|
922
|
+
value={input.value}
|
|
923
|
+
intent={intent}
|
|
924
|
+
{...removeUnwantedProps(rest)}
|
|
925
|
+
{...(hideValue ? { value: "" } : {})}
|
|
926
|
+
className={classNames(Classes.FILL, inputClassName)}
|
|
927
|
+
onValueChange={(numericVal, stringVal) => {
|
|
928
|
+
// needed for redux form to change value
|
|
929
|
+
input.onChange(stringVal);
|
|
930
|
+
//tnr: use this handler if you want to listen to all value changes!
|
|
931
|
+
onAnyNumberChange && onAnyNumberChange(numericVal);
|
|
932
|
+
}}
|
|
933
|
+
onButtonClick={function (numericVal, stringVal) {
|
|
934
|
+
handleBlurOrButtonClick(stringVal);
|
|
935
|
+
}}
|
|
936
|
+
onBlur={function (e) {
|
|
937
|
+
handleBlurOrButtonClick(e.target.value);
|
|
938
|
+
}}
|
|
939
|
+
/>
|
|
940
|
+
);
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
export const renderBlueprintRadioGroup = ({
|
|
944
|
+
input,
|
|
945
|
+
options,
|
|
946
|
+
onFieldSubmit,
|
|
947
|
+
...rest
|
|
948
|
+
}) => {
|
|
949
|
+
const optionsToUse = getOptions(options);
|
|
950
|
+
return (
|
|
951
|
+
<RadioGroup
|
|
952
|
+
{...input}
|
|
953
|
+
{...removeUnwantedProps(rest)}
|
|
954
|
+
selectedValue={input.value}
|
|
955
|
+
label={undefined} //removing label from radio group because our label already handles it
|
|
956
|
+
onChange={function (e, val, ...args) {
|
|
957
|
+
input.onChange(e, val, ...args);
|
|
958
|
+
onFieldSubmit(e.target ? e.target.value : val);
|
|
959
|
+
}}
|
|
960
|
+
options={optionsToUse}
|
|
961
|
+
/>
|
|
962
|
+
);
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
export class RenderReactColorPicker extends React.Component {
|
|
966
|
+
handleChange = (color) => {
|
|
967
|
+
const { input, onFieldSubmit } = this.props;
|
|
968
|
+
|
|
969
|
+
input.onChange(color.hex);
|
|
970
|
+
onFieldSubmit(color.hex);
|
|
971
|
+
};
|
|
972
|
+
|
|
973
|
+
render() {
|
|
974
|
+
const { input, onFieldSubmit, ...rest } = this.props;
|
|
975
|
+
return (
|
|
976
|
+
<Popover
|
|
977
|
+
position="bottom-right"
|
|
978
|
+
minimal
|
|
979
|
+
modifiers={popoverOverflowModifiers}
|
|
980
|
+
content={
|
|
981
|
+
<SketchPicker
|
|
982
|
+
className="tg-color-picker-selector"
|
|
983
|
+
color={input.value}
|
|
984
|
+
onChangeComplete={this.handleChange}
|
|
985
|
+
{...removeUnwantedProps(rest)}
|
|
986
|
+
/>
|
|
987
|
+
}
|
|
988
|
+
>
|
|
989
|
+
<div
|
|
990
|
+
style={{
|
|
991
|
+
padding: "7px",
|
|
992
|
+
margin: "1px",
|
|
993
|
+
background: "#fff",
|
|
994
|
+
borderRadius: "1px",
|
|
995
|
+
boxShadow: "0 0 0 1px rgba(0,0,0,.1)",
|
|
996
|
+
display: "inline-block",
|
|
997
|
+
cursor: "pointer",
|
|
998
|
+
}}
|
|
999
|
+
>
|
|
1000
|
+
<div
|
|
1001
|
+
className="tg-color-picker-selected-color"
|
|
1002
|
+
style={{
|
|
1003
|
+
width: "36px",
|
|
1004
|
+
height: "14px",
|
|
1005
|
+
borderRadius: "2px",
|
|
1006
|
+
background: `${input.value}`,
|
|
1007
|
+
}}
|
|
1008
|
+
/>
|
|
1009
|
+
</div>
|
|
1010
|
+
</Popover>
|
|
1011
|
+
);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// tgreen: This doesn't work because the async validate function will not be automatically rerun onSubmit
|
|
1016
|
+
// class AddAsyncValidate extends Component {
|
|
1017
|
+
// constructor(props) {
|
|
1018
|
+
// super(props);
|
|
1019
|
+
// this.state = {
|
|
1020
|
+
// asyncValidating: false
|
|
1021
|
+
// };
|
|
1022
|
+
// this.runAsyncValidationDebounced = debounce(this.runAsyncValidation, 500);
|
|
1023
|
+
// }
|
|
1024
|
+
|
|
1025
|
+
// componentDidUpdate(oldProps) {
|
|
1026
|
+
// const {
|
|
1027
|
+
// validateOnChange,
|
|
1028
|
+
// input: { name, value },
|
|
1029
|
+
// meta: { touched, form, dispatch }
|
|
1030
|
+
// } = this.props;
|
|
1031
|
+
// const newValue = value;
|
|
1032
|
+
// const oldValue = oldProps.input.value;
|
|
1033
|
+
// const valueHasChanged = newValue !== oldValue;
|
|
1034
|
+
// if (validateOnChange && valueHasChanged) {
|
|
1035
|
+
// this.runAsyncValidationDebounced(newValue);
|
|
1036
|
+
// }
|
|
1037
|
+
// // mark the input as touched after changing value
|
|
1038
|
+
// if (valueHasChanged && !touched) {
|
|
1039
|
+
// dispatch(touch(form, name));
|
|
1040
|
+
// }
|
|
1041
|
+
// }
|
|
1042
|
+
|
|
1043
|
+
// triggerAsyncValidation(error) {
|
|
1044
|
+
// const {
|
|
1045
|
+
// input: { name },
|
|
1046
|
+
// meta: { dispatch, form },
|
|
1047
|
+
// formAsyncErrors
|
|
1048
|
+
// } = this.props;
|
|
1049
|
+
// // this needs to get a fresh prop for formAsyncErrors otherwise it will get out of sync.
|
|
1050
|
+
// // the test will catch this.
|
|
1051
|
+
// dispatch(
|
|
1052
|
+
// stopAsyncValidation(form, {
|
|
1053
|
+
// ...formAsyncErrors,
|
|
1054
|
+
// [name]: error
|
|
1055
|
+
// })
|
|
1056
|
+
// );
|
|
1057
|
+
// }
|
|
1058
|
+
|
|
1059
|
+
// runAsyncValidation = async val => {
|
|
1060
|
+
// const {
|
|
1061
|
+
// input: { name },
|
|
1062
|
+
// meta: { dispatch, form },
|
|
1063
|
+
// asyncValidate
|
|
1064
|
+
// } = this.props;
|
|
1065
|
+
|
|
1066
|
+
// this.setState({
|
|
1067
|
+
// asyncValidating: true
|
|
1068
|
+
// });
|
|
1069
|
+
// // mark this field as invalid so that the user can not submit this form while async validating
|
|
1070
|
+
// this.triggerAsyncValidation("asyncValidating");
|
|
1071
|
+
// dispatch(startAsyncValidation(form));
|
|
1072
|
+
// const error = await asyncValidate(val);
|
|
1073
|
+
// this.triggerAsyncValidation(error);
|
|
1074
|
+
// // if there is no error then clear it from redux
|
|
1075
|
+
// if (!error) {
|
|
1076
|
+
// dispatch(clearAsyncError(form, name));
|
|
1077
|
+
// }
|
|
1078
|
+
|
|
1079
|
+
// this.setState({
|
|
1080
|
+
// asyncValidating: false
|
|
1081
|
+
// });
|
|
1082
|
+
|
|
1083
|
+
// return error;
|
|
1084
|
+
// };
|
|
1085
|
+
|
|
1086
|
+
// onBlur = (...args) => {
|
|
1087
|
+
// const { input } = this.props;
|
|
1088
|
+
// // always run this on input blur so that the user cannot submit a form with a field that has not finished validating
|
|
1089
|
+
// this.runAsyncValidation(input.value);
|
|
1090
|
+
// input.onBlur(...args);
|
|
1091
|
+
// };
|
|
1092
|
+
|
|
1093
|
+
// render() {
|
|
1094
|
+
// const { asyncValidating } = this.state;
|
|
1095
|
+
// const {
|
|
1096
|
+
// passedComponent: Component,
|
|
1097
|
+
// input,
|
|
1098
|
+
// formAsyncErrors, // don't pass through
|
|
1099
|
+
// dispatch, // don't pass through
|
|
1100
|
+
// ...rest
|
|
1101
|
+
// } = this.props;
|
|
1102
|
+
|
|
1103
|
+
// return (
|
|
1104
|
+
// <Component
|
|
1105
|
+
// {...rest}
|
|
1106
|
+
// input={{
|
|
1107
|
+
// ...input,
|
|
1108
|
+
// onBlur: this.onBlur
|
|
1109
|
+
// }}
|
|
1110
|
+
// asyncValidating={asyncValidating}
|
|
1111
|
+
// />
|
|
1112
|
+
// );
|
|
1113
|
+
// }
|
|
1114
|
+
// }
|
|
1115
|
+
|
|
1116
|
+
// const WrappedAddAsyncValidate = connect((state, { meta }) => {
|
|
1117
|
+
// return {
|
|
1118
|
+
// formAsyncErrors: getFormAsyncErrors(meta.form)(state)
|
|
1119
|
+
// };
|
|
1120
|
+
// })(AddAsyncValidate);
|
|
1121
|
+
|
|
1122
|
+
export function generateField(component, opts) {
|
|
1123
|
+
const compWithDefaultVal = withAbstractWrapper(component, opts);
|
|
1124
|
+
return function FieldMaker({
|
|
1125
|
+
name,
|
|
1126
|
+
isRequired,
|
|
1127
|
+
onFieldSubmit = noop,
|
|
1128
|
+
noRedux,
|
|
1129
|
+
// asyncValidate,
|
|
1130
|
+
...rest
|
|
1131
|
+
}) {
|
|
1132
|
+
const component = compWithDefaultVal;
|
|
1133
|
+
|
|
1134
|
+
const props = {
|
|
1135
|
+
onFieldSubmit,
|
|
1136
|
+
name,
|
|
1137
|
+
...(noRedux && {
|
|
1138
|
+
input: {
|
|
1139
|
+
onChange: rest.onChange || noop,
|
|
1140
|
+
onBlur: rest.onBlur || noop,
|
|
1141
|
+
value: rest.value,
|
|
1142
|
+
name,
|
|
1143
|
+
},
|
|
1144
|
+
}),
|
|
1145
|
+
component,
|
|
1146
|
+
...(isRequired && { validate: fieldRequired }),
|
|
1147
|
+
isRequired,
|
|
1148
|
+
...rest,
|
|
1149
|
+
};
|
|
1150
|
+
|
|
1151
|
+
// if (asyncValidate) {
|
|
1152
|
+
// props = {
|
|
1153
|
+
// ...props,
|
|
1154
|
+
// asyncValidate,
|
|
1155
|
+
// component: WrappedAddAsyncValidate,
|
|
1156
|
+
// passedComponent: component
|
|
1157
|
+
// };
|
|
1158
|
+
// }
|
|
1159
|
+
|
|
1160
|
+
return <Field {...props} />;
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
export const withAbstractWrapper = (ComponentToWrap, opts = {}) => {
|
|
1165
|
+
return (props) => {
|
|
1166
|
+
const {
|
|
1167
|
+
massageDefaultIdValue,
|
|
1168
|
+
generateDefaultValue,
|
|
1169
|
+
defaultValueByIdOverride,
|
|
1170
|
+
defaultValue: defaultValueFromProps,
|
|
1171
|
+
isRequired,
|
|
1172
|
+
...rest
|
|
1173
|
+
} = props;
|
|
1174
|
+
|
|
1175
|
+
//get is assign defaults mode
|
|
1176
|
+
//if assign default value mode then add on to the component
|
|
1177
|
+
const [defaultValCount, setDefaultValCount] = React.useState(0);
|
|
1178
|
+
const [defaultValueFromBackend, setDefault] = useState();
|
|
1179
|
+
const [allowUserOverride, setUserOverride] = useState(true);
|
|
1180
|
+
const [isLoadingDefaultValue, setLoadingDefaultValue] = useState(false);
|
|
1181
|
+
const { inAssignDefaultsMode, setAssignDefaultsMode } = useContext(
|
|
1182
|
+
AssignDefaultsModeContext
|
|
1183
|
+
);
|
|
1184
|
+
// tnr: we might want to grab this context object off the window in the future and have it live in lims by default
|
|
1185
|
+
// there is no reason for those vals to live in TRC. Example code below:
|
|
1186
|
+
// const workflowParams = useContext(window.__tgDefaultValParamsContext || defaultNullContext);
|
|
1187
|
+
const workflowParams = useContext(WorkflowDefaultParamsContext);
|
|
1188
|
+
|
|
1189
|
+
const caresAboutToolContext = generateDefaultValue?.params?.toolName;
|
|
1190
|
+
|
|
1191
|
+
const customParamsToUse = {
|
|
1192
|
+
...(caresAboutToolContext
|
|
1193
|
+
? { ...workflowDefaultParamsObj, ...workflowParams }
|
|
1194
|
+
: {}),
|
|
1195
|
+
...(generateDefaultValue ? generateDefaultValue.customParams : {}),
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1198
|
+
async function triggerGetDefault() {
|
|
1199
|
+
if (!defaultValueByIdOverride) {
|
|
1200
|
+
//if defaultValueByIdOverride is passed, we can skip over getting the value from the backend straight to massaging the default value
|
|
1201
|
+
if (!window.__triggerGetDefaultValueRequest) return;
|
|
1202
|
+
if (!generateDefaultValue) return;
|
|
1203
|
+
setLoadingDefaultValue(true);
|
|
1204
|
+
//custom params should match params keys. if not throw an error
|
|
1205
|
+
const doParamsMatch = isEqual(
|
|
1206
|
+
Object.keys({
|
|
1207
|
+
...(caresAboutToolContext ? workflowDefaultParamsObj : {}), //we don't want to compare these keys so we just spread them here
|
|
1208
|
+
...(generateDefaultValue.params || {}),
|
|
1209
|
+
}).sort(),
|
|
1210
|
+
Object.keys(customParamsToUse).sort()
|
|
1211
|
+
);
|
|
1212
|
+
if (!doParamsMatch) {
|
|
1213
|
+
console.warn(
|
|
1214
|
+
`Issue with generateDefaultValue. customParams don't match params`
|
|
1215
|
+
);
|
|
1216
|
+
console.warn(
|
|
1217
|
+
`generateDefaultValue.params:`,
|
|
1218
|
+
generateDefaultValue.params
|
|
1219
|
+
);
|
|
1220
|
+
console.warn(`generateDefaultValue.customParams:`, customParamsToUse);
|
|
1221
|
+
throw new Error(
|
|
1222
|
+
`Issue with generateDefaultValue code=${
|
|
1223
|
+
generateDefaultValue.code
|
|
1224
|
+
}: Difference detected with: ${difference(
|
|
1225
|
+
Object.keys(generateDefaultValue.params || {}),
|
|
1226
|
+
Object.keys(customParamsToUse || {})
|
|
1227
|
+
).join(
|
|
1228
|
+
", "
|
|
1229
|
+
)}. customParams passed into the field should match params (as defined in defaultValueConstants.js). See console for more details.`
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
try {
|
|
1235
|
+
let { defaultValue, allowUserOverride } = defaultValueByIdOverride
|
|
1236
|
+
? { defaultValue: defaultValueByIdOverride }
|
|
1237
|
+
: await window.__triggerGetDefaultValueRequest(
|
|
1238
|
+
generateDefaultValue.code,
|
|
1239
|
+
customParamsToUse
|
|
1240
|
+
);
|
|
1241
|
+
if (massageDefaultIdValue) {
|
|
1242
|
+
const massagedRes = await massageDefaultIdValue({
|
|
1243
|
+
defaultValueById: defaultValue,
|
|
1244
|
+
});
|
|
1245
|
+
if (massagedRes.defaultValue) {
|
|
1246
|
+
defaultValue = massagedRes.defaultValue;
|
|
1247
|
+
}
|
|
1248
|
+
if (massagedRes.preventUserOverrideFromBeingDisabled) {
|
|
1249
|
+
allowUserOverride = true;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
if (
|
|
1253
|
+
ComponentToWrap === renderBlueprintCheckbox ||
|
|
1254
|
+
ComponentToWrap === renderBlueprintSwitch
|
|
1255
|
+
) {
|
|
1256
|
+
setDefault(defaultValue === "true");
|
|
1257
|
+
} else {
|
|
1258
|
+
if (typeof defaultValue === "string") {
|
|
1259
|
+
// remove double spaces and leading/trailing
|
|
1260
|
+
defaultValue = defaultValue.replace(/\s+/g, " ").trim();
|
|
1261
|
+
}
|
|
1262
|
+
setDefault(defaultValue);
|
|
1263
|
+
}
|
|
1264
|
+
setUserOverride(allowUserOverride);
|
|
1265
|
+
setDefaultValCount(defaultValCount + 1);
|
|
1266
|
+
} catch (error) {
|
|
1267
|
+
console.error(`error aswf298f:`, error);
|
|
1268
|
+
}
|
|
1269
|
+
if (window.Cypress && window.Cypress.addFakeDefaultValueWait) {
|
|
1270
|
+
await fakeWait();
|
|
1271
|
+
}
|
|
1272
|
+
setLoadingDefaultValue(false);
|
|
1273
|
+
}
|
|
1274
|
+
// if generateDefaultValue, hit the backend for that value
|
|
1275
|
+
useDeepCompareEffect(() => {
|
|
1276
|
+
// if the input already has a value we don't want to override with the default value request
|
|
1277
|
+
if (rest.input.value) return;
|
|
1278
|
+
triggerGetDefault();
|
|
1279
|
+
}, [generateDefaultValue || {}]);
|
|
1280
|
+
// const asyncValidating = props.asyncValidating;
|
|
1281
|
+
const defaultProps = {
|
|
1282
|
+
...rest,
|
|
1283
|
+
defaultValue: defaultValueFromBackend || defaultValueFromProps,
|
|
1284
|
+
disabled: props.disabled || allowUserOverride === false,
|
|
1285
|
+
readOnly: props.readOnly || isLoadingDefaultValue,
|
|
1286
|
+
intent: getIntent(props),
|
|
1287
|
+
intentClass: getIntentClass(props),
|
|
1288
|
+
};
|
|
1289
|
+
|
|
1290
|
+
// don't show intent while async validating
|
|
1291
|
+
// if (asyncValidating) {
|
|
1292
|
+
// delete defaultProps.intent;
|
|
1293
|
+
// delete defaultProps.intentClass;
|
|
1294
|
+
// }
|
|
1295
|
+
|
|
1296
|
+
const startAssigningDefault = () =>
|
|
1297
|
+
window.__showAssignDefaultValueModal &&
|
|
1298
|
+
window.__showAssignDefaultValueModal({
|
|
1299
|
+
...props,
|
|
1300
|
+
generateDefaultValue: {
|
|
1301
|
+
...props.generateDefaultValue,
|
|
1302
|
+
customParams: customParamsToUse,
|
|
1303
|
+
},
|
|
1304
|
+
onFinish: () => {
|
|
1305
|
+
triggerGetDefault();
|
|
1306
|
+
},
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
return (
|
|
1310
|
+
<AbstractInput
|
|
1311
|
+
{...{
|
|
1312
|
+
...opts,
|
|
1313
|
+
defaultValCount,
|
|
1314
|
+
isRequired,
|
|
1315
|
+
...defaultProps,
|
|
1316
|
+
isLoadingDefaultValue,
|
|
1317
|
+
showGenerateDefaultDot:
|
|
1318
|
+
!inAssignDefaultsMode &&
|
|
1319
|
+
window.__showGenerateDefaultDot &&
|
|
1320
|
+
window.__showGenerateDefaultDot() &&
|
|
1321
|
+
!!generateDefaultValue,
|
|
1322
|
+
setAssignDefaultsMode,
|
|
1323
|
+
startAssigningDefault,
|
|
1324
|
+
assignDefaultButton: inAssignDefaultsMode && generateDefaultValue && (
|
|
1325
|
+
<Button
|
|
1326
|
+
onClick={startAssigningDefault}
|
|
1327
|
+
small
|
|
1328
|
+
style={{ background: "yellow", color: "black" }}
|
|
1329
|
+
>
|
|
1330
|
+
Assign Default
|
|
1331
|
+
</Button>
|
|
1332
|
+
),
|
|
1333
|
+
}}
|
|
1334
|
+
>
|
|
1335
|
+
<ComponentToWrap {...defaultProps} />
|
|
1336
|
+
</AbstractInput>
|
|
1337
|
+
);
|
|
1338
|
+
};
|
|
1339
|
+
};
|
|
1340
|
+
|
|
1341
|
+
export const InputField = generateField(RenderBlueprintInput);
|
|
1342
|
+
export const FileUploadField = generateField(renderFileUpload);
|
|
1343
|
+
export const DateInputField = generateField(renderBlueprintDateInput);
|
|
1344
|
+
export const DateRangeInputField = generateField(renderBlueprintDateRangeInput);
|
|
1345
|
+
export const CheckboxField = generateField(renderBlueprintCheckbox, {
|
|
1346
|
+
noOuterLabel: true,
|
|
1347
|
+
noFillField: true,
|
|
1348
|
+
});
|
|
1349
|
+
export const SwitchField = generateField(renderBlueprintSwitch, {
|
|
1350
|
+
noOuterLabel: true,
|
|
1351
|
+
noFillField: true,
|
|
1352
|
+
});
|
|
1353
|
+
export const TextareaField = generateField(renderBlueprintTextarea);
|
|
1354
|
+
export const SuggestField = generateField(renderSuggest);
|
|
1355
|
+
export const EditableTextField = generateField(renderBlueprintEditableText);
|
|
1356
|
+
export const NumericInputField = generateField(renderBlueprintNumericInput);
|
|
1357
|
+
export const RadioGroupField = generateField(renderBlueprintRadioGroup, {
|
|
1358
|
+
noFillField: true,
|
|
1359
|
+
});
|
|
1360
|
+
export const ReactSelectField = generateField(renderReactSelect);
|
|
1361
|
+
export const SelectField = generateField(renderSelect);
|
|
1362
|
+
export const ReactColorField = generateField(RenderReactColorPicker);
|
|
1363
|
+
|
|
1364
|
+
function getOptions(options) {
|
|
1365
|
+
return (
|
|
1366
|
+
options &&
|
|
1367
|
+
options.map(function (opt) {
|
|
1368
|
+
if (typeof opt === "string") {
|
|
1369
|
+
return { label: opt, value: opt };
|
|
1370
|
+
} else if (isNumber(opt)) return { label: opt.toString(), value: opt };
|
|
1371
|
+
return opt;
|
|
1372
|
+
})
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
function fakeWait() {
|
|
1377
|
+
const fakeWaitNum = isNumber(window.Cypress.addFakeDefaultValueWait)
|
|
1378
|
+
? window.Cypress.addFakeDefaultValueWait
|
|
1379
|
+
: 3000;
|
|
1380
|
+
|
|
1381
|
+
return new Promise((resolve) => {
|
|
1382
|
+
setTimeout(() => resolve(), fakeWaitNum);
|
|
1383
|
+
});
|
|
1384
|
+
}
|