@teselagen/ui 0.7.1 → 0.7.2-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { Suggest } from "@blueprintjs/select";
2
2
  import { Keys } from "@blueprintjs/core";
3
- import React, { useRef } from "react";
3
+ import React, { useCallback, useMemo, useRef } from "react";
4
4
  import classNames from "classnames";
5
5
  import { itemListPredicate } from "../TgSelect";
6
6
 
@@ -19,69 +19,92 @@ const itemRenderer = (i = "", { index, handleClick, modifiers }) => (
19
19
  </div>
20
20
  );
21
21
 
22
- const TgSuggest = props => {
23
- const {
24
- disabled,
25
- inputProps,
26
- intent,
27
- isLoading,
28
- isSimpleSearch,
29
- multi,
30
- noResultsText,
31
- notCreateable,
32
- onBlur,
33
- // eslint-disable-next-line @typescript-eslint/no-empty-function
34
- onChange = () => {},
35
- optionRenderer, //pull this one out here so it doesn't get passsed along
36
- options = [],
37
- placeholder,
38
- popoverProps,
39
- value = undefined,
40
- ...rest
41
- } = props;
42
-
22
+ const TgSuggest = ({
23
+ disabled,
24
+ inputProps: _inputProps,
25
+ intent,
26
+ isLoading,
27
+ isSimpleSearch,
28
+ multi,
29
+ noResultsText,
30
+ notCreateable,
31
+ onBlur,
32
+ onChange,
33
+ optionRenderer, //pull this one out here so it doesn't get passsed along
34
+ options,
35
+ placeholder,
36
+ popoverProps: _popoverProps,
37
+ value,
38
+ ...rest
39
+ }) => {
43
40
  const inputRef = useRef(null);
44
41
 
45
- const handleItemSelect = item => {
46
- inputRef.current && inputRef.current.blur();
47
- return onChange(item);
48
- };
42
+ const handleItemSelect = useCallback(
43
+ item => {
44
+ inputRef.current && inputRef.current.blur();
45
+ return onChange?.(item);
46
+ },
47
+ [onChange]
48
+ );
49
49
 
50
- const _itemListPredicate = (queryString, item) => {
51
- return itemListPredicate(queryString, item, isSimpleSearch);
52
- };
50
+ const _itemListPredicate = useCallback(
51
+ (queryString, item) => {
52
+ return itemListPredicate(queryString, item, isSimpleSearch);
53
+ },
54
+ [isSimpleSearch]
55
+ );
56
+
57
+ const popoverProps = useMemo(
58
+ () => ({
59
+ minimal: true,
60
+ className: classNames("tg-select", {
61
+ "tg-single-select": !multi
62
+ }),
63
+ wrapperTagName: "div",
64
+ usePortal: false,
65
+ canEscapeKeyClose: true,
66
+ ..._popoverProps
67
+ }),
68
+ [multi, _popoverProps]
69
+ );
70
+
71
+ const onKeyDown = useCallback(e => {
72
+ const { which } = e;
73
+ if (which === Keys.ENTER) {
74
+ e.preventDefault();
75
+ e.stopPropagation(); //this prevents the dialog it is in from closing
76
+ }
77
+ if (which === Keys.ESCAPE || which === Keys.TAB) {
78
+ // By default the escape key will not trigger a blur on the
79
+ // input element. It must be done explicitly.
80
+ if (inputRef.current != null) {
81
+ inputRef.current.blur();
82
+ }
83
+ e.preventDefault();
84
+ e.stopPropagation(); //this prevents dialog's it is in from closing
85
+ }
86
+ }, []);
87
+
88
+ const inputProps = useMemo(
89
+ () => ({
90
+ inputRef: n => {
91
+ if (n) inputRef.current = n;
92
+ },
93
+ placeholder: placeholder || "Type here...",
94
+ disabled: disabled, // tg: adding isLoading will cause the input to be blurred when using generic select asReactSelect (don't do it),
95
+ intent: intent,
96
+ ..._inputProps
97
+ }),
98
+ [disabled, _inputProps, intent, placeholder]
99
+ );
53
100
 
54
101
  return (
55
102
  <Suggest
56
103
  closeOnSelect
57
- items={options || []}
104
+ items={options}
58
105
  query={value}
59
- popoverProps={{
60
- minimal: true,
61
- className: classNames("tg-select", {
62
- "tg-single-select": !multi
63
- }),
64
- wrapperTagName: "div",
65
- usePortal: false,
66
- canEscapeKeyClose: true,
67
- ...popoverProps
68
- }}
69
- onKeyDown={e => {
70
- const { which } = e;
71
- if (which === Keys.ENTER) {
72
- e.preventDefault();
73
- e.stopPropagation(); //this prevents the dialog it is in from closing
74
- }
75
- if (which === Keys.ESCAPE || which === Keys.TAB) {
76
- // By default the escape key will not trigger a blur on the
77
- // input element. It must be done explicitly.
78
- if (inputRef.current != null) {
79
- inputRef.current.blur();
80
- }
81
- e.preventDefault();
82
- e.stopPropagation(); //this prevents dialog's it is in from closing
83
- }
84
- }}
106
+ popoverProps={popoverProps}
107
+ onKeyDown={onKeyDown}
85
108
  onItemSelect={handleItemSelect}
86
109
  noResults={null}
87
110
  resetOnSelect={false}
@@ -90,15 +113,7 @@ const TgSuggest = props => {
90
113
  itemListPredicate={_itemListPredicate}
91
114
  selectedItem={value}
92
115
  inputValueRenderer={item => item}
93
- inputProps={{
94
- inputRef: n => {
95
- if (n) inputRef.current = n;
96
- },
97
- placeholder: placeholder || "Type here...",
98
- disabled: disabled, // tg: adding isLoading will cause the input to be blurred when using generic select asReactSelect (don't do it),
99
- intent: intent,
100
- ...inputProps
101
- }}
116
+ inputProps={inputProps}
102
117
  isSimpleSearch={isSimpleSearch}
103
118
  onChange={onChange}
104
119
  {...rest}
@@ -1 +0,0 @@
1
- export function withAbstractWrapper(ComponentToWrap: any, opts?: {}): (props: any) => import("react/jsx-runtime").JSX.Element;
@@ -1,388 +0,0 @@
1
- import { Button, FormGroup, Position, Tooltip } from "@blueprintjs/core";
2
- import { useCallback, useContext, useEffect, useRef, useState } from "react";
3
- import {
4
- AssignDefaultsModeContext,
5
- WorkflowDefaultParamsContext,
6
- workflowDefaultParamsObj
7
- } from "../AssignDefaultsModeContext";
8
- import { difference, isEqual, kebabCase } from "lodash-es";
9
- import {
10
- fakeWait,
11
- getIntent,
12
- getIntentClass,
13
- LabelWithTooltipInfo
14
- } from "./utils";
15
- import useDeepCompareEffect from "use-deep-compare-effect";
16
- import { change } from "redux-form";
17
- import popoverOverflowModifiers from "../utils/popoverOverflowModifiers";
18
- import classNames from "classnames";
19
-
20
- const AbstractInput = props => {
21
- const {
22
- defaultValue,
23
- meta: { dispatch, form, touched, error, warning },
24
- onDefaultValChanged,
25
- onFieldSubmit,
26
- children,
27
- tooltipProps,
28
- tooltipError,
29
- disabled,
30
- intent,
31
- tooltipInfo,
32
- label,
33
- inlineLabel,
34
- isLabelTooltip,
35
- secondaryLabel,
36
- className,
37
- showErrorIfUntouched,
38
- asyncValidating,
39
- containerStyle,
40
- leftEl,
41
- rightEl,
42
- labelStyle,
43
- noOuterLabel,
44
- fileLimit,
45
- noMarginBottom,
46
- assignDefaultButton,
47
- showGenerateDefaultDot,
48
- setAssignDefaultsMode,
49
- startAssigningDefault,
50
- input,
51
- noFillField,
52
- isRequired,
53
- isLoadingDefaultValue,
54
- enableReinitialize,
55
- defaultValCount
56
- } = props;
57
-
58
- const prevProps = useRef({ defaultValue, defaultValCount });
59
-
60
- const updateDefaultValue = useCallback(() => {
61
- dispatch(change(form, input.name, defaultValue));
62
- onDefaultValChanged &&
63
- onDefaultValChanged(defaultValue, input.name, form, props);
64
- onFieldSubmit && onFieldSubmit(defaultValue);
65
- }, [
66
- defaultValue,
67
- dispatch,
68
- form,
69
- input.name,
70
- onDefaultValChanged,
71
- onFieldSubmit,
72
- props
73
- ]);
74
-
75
- useEffect(() => {
76
- if (
77
- ((input.value !== false && !input.value) || enableReinitialize) &&
78
- defaultValue !== undefined
79
- ) {
80
- updateDefaultValue();
81
- }
82
- }, [defaultValue, enableReinitialize, input.value, updateDefaultValue]);
83
-
84
- useEffect(() => {
85
- const {
86
- defaultValue: oldDefaultValue,
87
- defaultValCount: oldDefaultValCount
88
- } = prevProps.current;
89
-
90
- if (
91
- ((input.value !== false && !input.value) ||
92
- enableReinitialize ||
93
- defaultValCount !== oldDefaultValCount) &&
94
- !isEqual(defaultValue, oldDefaultValue)
95
- ) {
96
- updateDefaultValue();
97
- }
98
-
99
- prevProps.current = { defaultValue, defaultValCount };
100
- }, [
101
- defaultValue,
102
- defaultValCount,
103
- enableReinitialize,
104
- input.value,
105
- updateDefaultValue
106
- ]);
107
-
108
- // if our custom field level validation is happening then we don't want to show the error visually
109
- const showError =
110
- (touched || showErrorIfUntouched) && error && !asyncValidating;
111
- const showWarning = (touched || showErrorIfUntouched) && warning;
112
- let componentToWrap =
113
- isLabelTooltip || tooltipError ? (
114
- <Tooltip
115
- disabled={isLabelTooltip ? false : !showError}
116
- intent={isLabelTooltip ? "none" : error ? "danger" : "warning"}
117
- content={isLabelTooltip ? label : error || warning}
118
- position={Position.TOP}
119
- modifiers={popoverOverflowModifiers}
120
- {...tooltipProps}
121
- >
122
- {children}
123
- </Tooltip>
124
- ) : (
125
- children
126
- );
127
- const testClassName = "tg-test-" + kebabCase(input.name);
128
- if (noFillField) {
129
- componentToWrap = <div className="tg-no-fill-field">{componentToWrap}</div>;
130
- }
131
-
132
- let helperText;
133
- if (!tooltipError) {
134
- if (showError) {
135
- helperText = error;
136
- } else if (showWarning) {
137
- helperText = warning;
138
- }
139
- }
140
-
141
- // if in a cypress test show message so that inputs will not be interactable
142
- if (window.Cypress && isLoadingDefaultValue) {
143
- return "Loading default value...";
144
- }
145
-
146
- let labelInfo = secondaryLabel;
147
-
148
- const hasOuterLabel = !noOuterLabel && !isLabelTooltip;
149
- function getFileLimitInfo() {
150
- if (!fileLimit) return "";
151
- return `max ${fileLimit} file${fileLimit === 1 ? "" : "s"}`;
152
- }
153
-
154
- if (isRequired && hasOuterLabel && label && !labelInfo) {
155
- labelInfo = `(required${fileLimit ? `, ${getFileLimitInfo()}` : ""})`;
156
- } else if (!labelInfo && fileLimit) {
157
- labelInfo = `(${getFileLimitInfo()})`;
158
- }
159
-
160
- return (
161
- <FormGroup
162
- className={classNames(className, testClassName, {
163
- "tg-flex-form-content": leftEl || rightEl,
164
- "tg-tooltipError": tooltipError,
165
- "tg-has-error": showError && error
166
- })}
167
- disabled={disabled}
168
- helperText={helperText}
169
- intent={intent}
170
- label={
171
- hasOuterLabel && (
172
- <LabelWithTooltipInfo
173
- labelStyle={labelStyle}
174
- label={label}
175
- tooltipInfo={tooltipInfo}
176
- />
177
- )
178
- }
179
- inline={inlineLabel}
180
- labelInfo={labelInfo}
181
- style={{
182
- ...(noMarginBottom && { marginBottom: 0 }),
183
- ...containerStyle
184
- }}
185
- >
186
- {showGenerateDefaultDot && (
187
- <div style={{ zIndex: 10, position: "relative", height: 0, width: 0 }}>
188
- <div style={{ position: "absolute", left: "0px", top: "0px" }}>
189
- <Tooltip
190
- modifiers={popoverOverflowModifiers}
191
- content="Allows a Default to be Set. Click to Enter Set Default Mode (or press Shift+D when outside the input field)"
192
- >
193
- <div
194
- onClick={() => {
195
- setAssignDefaultsMode(true);
196
- startAssigningDefault();
197
- }}
198
- className="generateDefaultDot"
199
- ></div>
200
- </Tooltip>
201
- </div>
202
- </div>
203
- )}
204
- {assignDefaultButton}
205
- {leftEl} {componentToWrap} {rightEl}
206
- </FormGroup>
207
- );
208
- };
209
-
210
- export const withAbstractWrapper = (ComponentToWrap, opts = {}) => {
211
- return props => {
212
- const {
213
- massageDefaultIdValue,
214
- generateDefaultValue,
215
- defaultValueByIdOverride,
216
- defaultValue: defaultValueFromProps,
217
- isRequired,
218
- ...rest
219
- } = props;
220
-
221
- //get is assign defaults mode
222
- //if assign default value mode then add on to the component
223
- const [defaultValCount, setDefaultValCount] = useState(0);
224
- const [defaultValueFromBackend, setDefault] = useState();
225
- const [allowUserOverride, setUserOverride] = useState(true);
226
- const [isLoadingDefaultValue, setLoadingDefaultValue] = useState(false);
227
- const { inAssignDefaultsMode, setAssignDefaultsMode } = useContext(
228
- AssignDefaultsModeContext
229
- );
230
- // tnr: we might want to grab this context object off the window in the future and have it live in lims by default
231
- // there is no reason for those vals to live in TRC. Example code below:
232
- // const workflowParams = useContext(window.__tgDefaultValParamsContext || defaultNullContext);
233
- const workflowParams = useContext(WorkflowDefaultParamsContext);
234
-
235
- const caresAboutToolContext = generateDefaultValue?.params?.toolName;
236
-
237
- const customParamsToUse = {
238
- ...(caresAboutToolContext
239
- ? { ...workflowDefaultParamsObj, ...workflowParams }
240
- : {}),
241
- ...(generateDefaultValue ? generateDefaultValue.customParams : {})
242
- };
243
-
244
- async function triggerGetDefault() {
245
- if (!defaultValueByIdOverride) {
246
- //if defaultValueByIdOverride is passed, we can skip over getting the value from the backend straight to massaging the default value
247
- if (!window.__triggerGetDefaultValueRequest) return;
248
- if (!generateDefaultValue) return;
249
- setLoadingDefaultValue(true);
250
- //custom params should match params keys. if not throw an error
251
- const doParamsMatch = isEqual(
252
- Object.keys({
253
- ...(caresAboutToolContext ? workflowDefaultParamsObj : {}), //we don't want to compare these keys so we just spread them here
254
- ...(generateDefaultValue.params || {})
255
- }).sort(),
256
- Object.keys(customParamsToUse).sort()
257
- );
258
- if (!doParamsMatch) {
259
- console.warn(
260
- `Issue with generateDefaultValue. customParams don't match params`
261
- );
262
- console.warn(
263
- `generateDefaultValue.params:`,
264
- generateDefaultValue.params
265
- );
266
- console.warn(`generateDefaultValue.customParams:`, customParamsToUse);
267
- throw new Error(
268
- `Issue with generateDefaultValue code=${
269
- generateDefaultValue.code
270
- }: Difference detected with: ${difference(
271
- Object.keys(generateDefaultValue.params || {}),
272
- Object.keys(customParamsToUse || {})
273
- ).join(
274
- ", "
275
- )}. customParams passed into the field should match params (as defined in defaultValueConstants.js). See console for more details.`
276
- );
277
- }
278
- }
279
-
280
- try {
281
- let { defaultValue, allowUserOverride } = defaultValueByIdOverride
282
- ? { defaultValue: defaultValueByIdOverride }
283
- : await window.__triggerGetDefaultValueRequest(
284
- generateDefaultValue.code,
285
- customParamsToUse
286
- );
287
- if (massageDefaultIdValue) {
288
- const massagedRes = await massageDefaultIdValue({
289
- defaultValueById: defaultValue
290
- });
291
- if (massagedRes.defaultValue) {
292
- defaultValue = massagedRes.defaultValue;
293
- }
294
- if (massagedRes.preventUserOverrideFromBeingDisabled) {
295
- allowUserOverride = true;
296
- }
297
- }
298
-
299
- // TODO:Add ths back in when we have a better way to determine if a field is a checkbox or switch
300
- // if (
301
- // "false" === false
302
- // // ComponentToWrap === renderBlueprintCheckbox ||
303
- // // ComponentToWrap === renderBlueprintSwitch
304
- // ) {
305
- // setDefault(defaultValue === "true");
306
- // } else {
307
- if (typeof defaultValue === "string") {
308
- // remove double spaces and leading/trailing
309
- defaultValue = defaultValue.replace(/\s+/g, " ").trim();
310
- }
311
- setDefault(defaultValue);
312
- // }
313
- setUserOverride(allowUserOverride);
314
- setDefaultValCount(defaultValCount + 1);
315
- } catch (error) {
316
- console.error(`error aswf298f:`, error);
317
- }
318
- if (window.Cypress && window.Cypress.addFakeDefaultValueWait) {
319
- await fakeWait();
320
- }
321
- setLoadingDefaultValue(false);
322
- }
323
- // if generateDefaultValue, hit the backend for that value
324
- useDeepCompareEffect(() => {
325
- // if the input already has a value we don't want to override with the default value request
326
- if (rest.input.value) return;
327
- triggerGetDefault();
328
- }, [generateDefaultValue || {}]);
329
- // const asyncValidating = props.asyncValidating;
330
- const defaultProps = {
331
- ...rest,
332
- defaultValue: defaultValueFromBackend || defaultValueFromProps,
333
- disabled: props.disabled || allowUserOverride === false,
334
- readOnly: props.readOnly || isLoadingDefaultValue,
335
- intent: getIntent(props),
336
- intentClass: getIntentClass(props)
337
- };
338
-
339
- // don't show intent while async validating
340
- // if (asyncValidating) {
341
- // delete defaultProps.intent;
342
- // delete defaultProps.intentClass;
343
- // }
344
-
345
- const startAssigningDefault = () =>
346
- window.__showAssignDefaultValueModal &&
347
- window.__showAssignDefaultValueModal({
348
- ...props,
349
- generateDefaultValue: {
350
- ...props.generateDefaultValue,
351
- customParams: customParamsToUse
352
- },
353
- onFinish: () => {
354
- triggerGetDefault();
355
- }
356
- });
357
-
358
- return (
359
- <AbstractInput
360
- {...{
361
- ...opts,
362
- defaultValCount,
363
- isRequired,
364
- ...defaultProps,
365
- isLoadingDefaultValue,
366
- showGenerateDefaultDot:
367
- !inAssignDefaultsMode &&
368
- window.__showGenerateDefaultDot &&
369
- window.__showGenerateDefaultDot() &&
370
- !!generateDefaultValue,
371
- setAssignDefaultsMode,
372
- startAssigningDefault,
373
- assignDefaultButton: inAssignDefaultsMode && generateDefaultValue && (
374
- <Button
375
- onClick={startAssigningDefault}
376
- small
377
- style={{ background: "yellow", color: "black" }}
378
- >
379
- Assign Default
380
- </Button>
381
- )
382
- }}
383
- >
384
- <ComponentToWrap {...defaultProps} />
385
- </AbstractInput>
386
- );
387
- };
388
- };