@onehat/ui 0.4.34 → 0.4.35
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/package.json +1 -1
- package/src/Components/Editor/Editor.js +3 -1
- package/src/Components/Form/Form.js +6 -11
- package/src/Components/Grid/Grid.js +3 -2
- package/src/Components/Grid/GridRow.js +1 -1
- package/src/Components/Hoc/Secondary/withSecondaryEditor.js +51 -19
- package/src/Components/Hoc/Secondary/withSecondarySelection.js +29 -23
- package/src/Components/Hoc/withEditor.js +6 -1
- package/src/Components/Hoc/withPdfButtons.js +0 -1
- package/src/Components/Hoc/withSelection.js +4 -3
- package/src/Functions/Cypress/dom_functions.js +20 -2
- package/src/Functions/Cypress/form_functions.js +178 -172
- package/src/Functions/Cypress/grid_functions.js +18 -9
package/package.json
CHANGED
|
@@ -65,8 +65,7 @@ import _ from 'lodash';
|
|
|
65
65
|
// Form is embedded on screen in some other way. Mainly use startingValues, items, validator
|
|
66
66
|
|
|
67
67
|
function Form(props) {
|
|
68
|
-
const
|
|
69
|
-
{
|
|
68
|
+
const {
|
|
70
69
|
editorType = EDITOR_TYPE__WINDOWED, // EDITOR_TYPE__INLINE | EDITOR_TYPE__WINDOWED | EDITOR_TYPE__SIDE | EDITOR_TYPE__SMART | EDITOR_TYPE__PLAIN
|
|
71
70
|
startingValues = {},
|
|
72
71
|
items = [], // Columns, FieldSets, Fields, etc to define the form
|
|
@@ -114,7 +113,7 @@ function Form(props) {
|
|
|
114
113
|
// withEditor
|
|
115
114
|
isEditorViewOnly = false,
|
|
116
115
|
isSaving = false,
|
|
117
|
-
getEditorMode,
|
|
116
|
+
getEditorMode = () => {},
|
|
118
117
|
onCancel,
|
|
119
118
|
onSave,
|
|
120
119
|
onClose,
|
|
@@ -247,16 +246,14 @@ function Form(props) {
|
|
|
247
246
|
} else {
|
|
248
247
|
// editor is not defined, fall back to property definition
|
|
249
248
|
if (isEditable) {
|
|
250
|
-
const
|
|
251
|
-
{
|
|
249
|
+
const {
|
|
252
250
|
type: t,
|
|
253
251
|
...p
|
|
254
252
|
} = propertyDef?.editorType;
|
|
255
253
|
type = t;
|
|
256
254
|
editorTypeProps = p;
|
|
257
255
|
} else if (propertyDef?.viewerType) {
|
|
258
|
-
const
|
|
259
|
-
{
|
|
256
|
+
const {
|
|
260
257
|
type: t,
|
|
261
258
|
...p
|
|
262
259
|
} = propertyDef?.viewerType;
|
|
@@ -504,16 +501,14 @@ function Form(props) {
|
|
|
504
501
|
}
|
|
505
502
|
if (!type) {
|
|
506
503
|
if (isEditable) {
|
|
507
|
-
const
|
|
508
|
-
{
|
|
504
|
+
const {
|
|
509
505
|
type: t,
|
|
510
506
|
...p
|
|
511
507
|
} = propertyDef?.editorType;
|
|
512
508
|
type = t;
|
|
513
509
|
editorTypeProps = p;
|
|
514
510
|
} else if (propertyDef?.viewerType) {
|
|
515
|
-
const
|
|
516
|
-
{
|
|
511
|
+
const {
|
|
517
512
|
type: t,
|
|
518
513
|
...p
|
|
519
514
|
} = propertyDef?.viewerType;
|
|
@@ -391,6 +391,7 @@ function GridComponent(props) {
|
|
|
391
391
|
|
|
392
392
|
let rowComponent =
|
|
393
393
|
<Pressable
|
|
394
|
+
dataSet={{ ix: index }}
|
|
394
395
|
{...testProps(getRowTestId ? getRowTestId(row) : ((Repository ? Repository.schema.name : 'GridRow') + '-' + item?.id))}
|
|
395
396
|
onPress={(e) => {
|
|
396
397
|
if (e.preventDefault && e.cancelable) {
|
|
@@ -467,7 +468,7 @@ function GridComponent(props) {
|
|
|
467
468
|
onContextMenu(item, e, selection);
|
|
468
469
|
}
|
|
469
470
|
}}
|
|
470
|
-
className="flex-row grow">
|
|
471
|
+
className="Pressable Row flex-row grow">
|
|
471
472
|
{({
|
|
472
473
|
hovered,
|
|
473
474
|
focused,
|
|
@@ -1182,7 +1183,7 @@ function GridComponent(props) {
|
|
|
1182
1183
|
initialNumToRender={initialNumToRender}
|
|
1183
1184
|
initialScrollIndex={0}
|
|
1184
1185
|
renderItem={renderRow}
|
|
1185
|
-
className="
|
|
1186
|
+
className="bg-grey-100"
|
|
1186
1187
|
{...flatListProps}
|
|
1187
1188
|
/>;
|
|
1188
1189
|
|
|
@@ -319,7 +319,7 @@ function GridRow(props) {
|
|
|
319
319
|
rowClassName += ' border-4 border-[#0ff]';
|
|
320
320
|
}
|
|
321
321
|
return <HStackNative
|
|
322
|
-
{...testProps('
|
|
322
|
+
{...testProps('Row ' + (isSelected ? 'row-selected' : ''))}
|
|
323
323
|
{...rowProps}
|
|
324
324
|
key={hash}
|
|
325
325
|
className={rowClassName}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
EDITOR_TYPE__SIDE,
|
|
14
14
|
EDITOR_TYPE__INLINE,
|
|
15
15
|
} from '../../../Constants/Editor.js';
|
|
16
|
+
import useForceUpdate from '../../../Hooks/useForceUpdate.js'
|
|
16
17
|
import Button from '../../Buttons/Button.js';
|
|
17
18
|
import UiGlobals from '../../../UiGlobals.js';
|
|
18
19
|
import _ from 'lodash';
|
|
@@ -27,7 +28,6 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
27
28
|
return <WrappedComponent {...props} ref={ref} isTree={isTree} />;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
let [secondaryEditorMode, secondarySetEditorMode] = useState(EDITOR_MODE__VIEW); // Can change below, so use 'let'
|
|
31
31
|
const {
|
|
32
32
|
secondaryUserCanEdit = true, // not permissions, but capability
|
|
33
33
|
secondaryUserCanView = true,
|
|
@@ -72,6 +72,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
72
72
|
|
|
73
73
|
// withSecondarySelection
|
|
74
74
|
secondarySelection,
|
|
75
|
+
secondaryGetSelection,
|
|
75
76
|
secondarySetSelection,
|
|
76
77
|
|
|
77
78
|
// withAlert
|
|
@@ -79,17 +80,25 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
79
80
|
confirm,
|
|
80
81
|
hideAlert,
|
|
81
82
|
} = props,
|
|
83
|
+
forceUpdate = useForceUpdate(),
|
|
82
84
|
secondaryListeners = useRef({}),
|
|
83
85
|
secondaryEditorStateRef = useRef(),
|
|
84
86
|
secondaryNewEntityDisplayValueRef = useRef(),
|
|
87
|
+
secondaryEditorModeRef = useRef(EDITOR_MODE__VIEW),
|
|
88
|
+
secondaryIsIgnoreNextSelectionChangeRef = useRef(false),
|
|
85
89
|
secondaryModel = SecondaryRepository?.schema?.name,
|
|
86
90
|
[secondaryCurrentRecord, secondarySetCurrentRecord] = useState(null),
|
|
87
91
|
[secondaryIsAdding, setIsAdding] = useState(false),
|
|
88
92
|
[secondaryIsSaving, setIsSaving] = useState(false),
|
|
89
93
|
[secondaryIsEditorShown, secondarySetIsEditorShownRaw] = useState(false),
|
|
90
94
|
[secondaryIsEditorViewOnly, setIsEditorViewOnly] = useState(secondaryCanEditorViewOnly), // current state of whether editor is in view-only mode
|
|
91
|
-
[secondaryIsIgnoreNextSelectionChange, setSecondaryIsIgnoreNextSelectionChange] = useState(false),
|
|
92
95
|
[secondaryLastSelection, setLastSelection] = useState(),
|
|
96
|
+
secondarySetIsIgnoreNextSelectionChange = (bool) => {
|
|
97
|
+
secondaryIsIgnoreNextSelectionChangeRef.current = bool;
|
|
98
|
+
},
|
|
99
|
+
secondaryGetIsIgnoreNextSelectionChange = () => {
|
|
100
|
+
return secondaryIsIgnoreNextSelectionChangeRef.current;
|
|
101
|
+
},
|
|
93
102
|
secondarySetIsEditorShown = (bool) => {
|
|
94
103
|
secondarySetIsEditorShownRaw(bool);
|
|
95
104
|
if (!bool && secondaryOnEditorClose) {
|
|
@@ -100,8 +109,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
100
109
|
function doIt() {
|
|
101
110
|
secondarySetSelection(newSelection);
|
|
102
111
|
}
|
|
103
|
-
const
|
|
104
|
-
|
|
112
|
+
const
|
|
113
|
+
formState = secondaryEditorStateRef.current,
|
|
114
|
+
secondarySelection = secondaryGetSelection();
|
|
115
|
+
if (!_.isEmpty(formState?.dirtyFields) && newSelection !== secondarySelection && secondaryGetEditorMode() === EDITOR_MODE__EDIT) {
|
|
105
116
|
confirm('This record has unsaved changes. Are you sure you want to cancel editing? Changes will be lost.', doIt);
|
|
106
117
|
} else if (secondarySelection && secondarySelection[0] && !secondarySelection[0].isDestroyed && (secondarySelection[0]?.isPhantom || secondarySelection[0]?.isRemotePhantom)) {
|
|
107
118
|
confirm('This new record is unsaved. Are you sure you want to cancel editing? Changes will be lost.', async () => {
|
|
@@ -119,6 +130,15 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
119
130
|
secondaryListeners.current = obj;
|
|
120
131
|
// forceUpdate(); // we don't want to get into an infinite loop of renders. Simply directly assign the secondaryListeners in every child render
|
|
121
132
|
},
|
|
133
|
+
secondaryGetEditorMode = () => {
|
|
134
|
+
return secondaryEditorModeRef.current;
|
|
135
|
+
},
|
|
136
|
+
secondarySetEditorMode = (mode) => {
|
|
137
|
+
if (secondaryEditorModeRef.current !== mode) {
|
|
138
|
+
secondaryEditorModeRef.current = mode;
|
|
139
|
+
forceUpdate();
|
|
140
|
+
}
|
|
141
|
+
},
|
|
122
142
|
getNewEntityDisplayValue = () => {
|
|
123
143
|
return secondaryNewEntityDisplayValueRef.current;
|
|
124
144
|
},
|
|
@@ -128,6 +148,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
128
148
|
return;
|
|
129
149
|
}
|
|
130
150
|
|
|
151
|
+
const secondarySelection = secondaryGetSelection();
|
|
131
152
|
let addValues = values;
|
|
132
153
|
|
|
133
154
|
if (SecondaryRepository?.isLoading) {
|
|
@@ -202,7 +223,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
202
223
|
setIsSaving(true);
|
|
203
224
|
const entity = await SecondaryRepository.add(addValues, false, true);
|
|
204
225
|
setIsSaving(false);
|
|
205
|
-
|
|
226
|
+
secondarySetIsIgnoreNextSelectionChange(true);
|
|
206
227
|
secondarySetSelection([entity]);
|
|
207
228
|
if (getListeners().onAfterAdd) {
|
|
208
229
|
await getListeners().onAfterAdd(entity);
|
|
@@ -225,6 +246,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
225
246
|
showPermissionsError(EDIT, secondaryModel);
|
|
226
247
|
return;
|
|
227
248
|
}
|
|
249
|
+
const secondarySelection = secondaryGetSelection();
|
|
228
250
|
if (_.isEmpty(secondarySelection) || (_.isArray(secondarySelection) && (secondarySelection.length > 1 || secondarySelection[0]?.isDestroyed))) {
|
|
229
251
|
return;
|
|
230
252
|
}
|
|
@@ -247,6 +269,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
247
269
|
if (_.isFunction(args)) {
|
|
248
270
|
cb = args;
|
|
249
271
|
}
|
|
272
|
+
const secondarySelection = secondaryGetSelection();
|
|
250
273
|
if (_.isEmpty(secondarySelection) || (_.isArray(secondarySelection) && (secondarySelection.length > 1 || secondarySelection[0]?.isDestroyed))) {
|
|
251
274
|
return;
|
|
252
275
|
}
|
|
@@ -305,6 +328,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
305
328
|
showPermissionsError(DELETE, secondaryModel);
|
|
306
329
|
return;
|
|
307
330
|
}
|
|
331
|
+
const secondarySelection = secondaryGetSelection();
|
|
308
332
|
if (getListeners().onBeforeDelete) {
|
|
309
333
|
const listenerResult = await getListeners().onBeforeDelete(secondarySelection);
|
|
310
334
|
if (listenerResult === false) {
|
|
@@ -345,6 +369,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
345
369
|
|
|
346
370
|
// check permissions for view
|
|
347
371
|
|
|
372
|
+
const secondarySelection = secondaryGetSelection();
|
|
348
373
|
if (secondarySelection.length !== 1) {
|
|
349
374
|
return;
|
|
350
375
|
}
|
|
@@ -367,7 +392,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
367
392
|
|
|
368
393
|
// check permissions for duplicate
|
|
369
394
|
|
|
370
|
-
|
|
395
|
+
const secondarySelection = secondaryGetSelection();
|
|
371
396
|
if (secondarySelection.length !== 1) {
|
|
372
397
|
return;
|
|
373
398
|
}
|
|
@@ -381,29 +406,32 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
381
406
|
rawValues = _.omit(entity.getOriginalData(), idProperty);
|
|
382
407
|
rawValues.id = null; // unset the id of the duplicate
|
|
383
408
|
const duplicate = await SecondaryRepository.add(rawValues, false, true);
|
|
384
|
-
|
|
409
|
+
secondarySetIsIgnoreNextSelectionChange(true);
|
|
385
410
|
secondarySetSelection([duplicate]);
|
|
386
411
|
secondarySetEditorMode(EDITOR_MODE__EDIT);
|
|
387
412
|
secondarySetIsEditorShown(true);
|
|
388
413
|
},
|
|
389
414
|
onRemoteDuplicate = async () => {
|
|
390
415
|
const
|
|
416
|
+
secondarySelection = secondaryGetSelection(),
|
|
391
417
|
entity = secondarySelection[0],
|
|
392
418
|
duplicateEntity = await SecondaryRepository.remoteDuplicate(entity);
|
|
393
419
|
|
|
394
|
-
|
|
420
|
+
secondarySetIsIgnoreNextSelectionChange(true);
|
|
395
421
|
secondarySetSelection([duplicateEntity]);
|
|
396
422
|
secondaryDoEdit();
|
|
397
423
|
},
|
|
398
424
|
secondaryDoEditorSave = async (data, e) => {
|
|
399
|
-
let mode =
|
|
425
|
+
let mode = secondaryGetEditorMode() === EDITOR_MODE__ADD ? ADD : EDIT;
|
|
400
426
|
if (canUser && !canUser(mode, secondaryModel)) {
|
|
401
427
|
showPermissionsError(mode, secondaryModel);
|
|
402
428
|
return;
|
|
403
429
|
}
|
|
404
430
|
|
|
405
431
|
// NOTE: The Form submits onSave for both adds (when not isAutoSsave) and edits.
|
|
406
|
-
const
|
|
432
|
+
const
|
|
433
|
+
secondarySelection = secondaryGetSelection(),
|
|
434
|
+
isSingle = secondarySelection.length === 1;
|
|
407
435
|
let useStaged = false;
|
|
408
436
|
if (isSingle) {
|
|
409
437
|
// just update this one entity
|
|
@@ -450,7 +478,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
450
478
|
if (secondaryOnChange) {
|
|
451
479
|
secondaryOnChange(secondarySelection);
|
|
452
480
|
}
|
|
453
|
-
if (
|
|
481
|
+
if (secondaryGetEditorMode() === EDITOR_MODE__ADD) {
|
|
454
482
|
if (secondaryOnAdd) {
|
|
455
483
|
await secondaryOnAdd(secondarySelection);
|
|
456
484
|
}
|
|
@@ -463,7 +491,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
463
491
|
} else {
|
|
464
492
|
secondarySetEditorMode(EDITOR_MODE__VIEW);
|
|
465
493
|
}
|
|
466
|
-
} else if (
|
|
494
|
+
} else if (secondaryGetEditorMode() === EDITOR_MODE__EDIT) {
|
|
467
495
|
if (getListeners().onAfterEdit) {
|
|
468
496
|
await getListeners().onAfterEdit(secondarySelection);
|
|
469
497
|
}
|
|
@@ -479,6 +507,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
479
507
|
secondaryDoEditorCancel = () => {
|
|
480
508
|
async function doIt() {
|
|
481
509
|
const
|
|
510
|
+
secondarySelection = secondaryGetSelection(),
|
|
482
511
|
isSingle = secondarySelection.length === 1,
|
|
483
512
|
isPhantom = secondarySelection[0] && !secondarySelection[0]?.isDestroyed && secondarySelection[0].isPhantom;
|
|
484
513
|
if (isSingle && isPhantom) {
|
|
@@ -517,9 +546,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
517
546
|
secondarySetIsEditorShown(false);
|
|
518
547
|
});
|
|
519
548
|
},
|
|
520
|
-
calculateEditorMode = (
|
|
549
|
+
calculateEditorMode = () => {
|
|
521
550
|
|
|
522
|
-
let
|
|
551
|
+
let secondaryIsIgnoreNextSelectionChange = secondaryGetIsIgnoreNextSelectionChange(),
|
|
552
|
+
doStayInEditModeOnSelectionChange = secondaryStayInEditModeOnSelectionChange;
|
|
523
553
|
if (!_.isNil(UiGlobals.stayInEditModeOnSelectionChange)) {
|
|
524
554
|
// allow global override to for this property
|
|
525
555
|
doStayInEditModeOnSelectionChange = UiGlobals.stayInEditModeOnSelectionChange;
|
|
@@ -529,13 +559,14 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
529
559
|
}
|
|
530
560
|
|
|
531
561
|
// calculateEditorMode gets called only on selection changes
|
|
562
|
+
const secondarySelection = secondaryGetSelection();
|
|
532
563
|
let mode;
|
|
533
564
|
if (secondaryEditorType === EDITOR_TYPE__SIDE && !_.isNil(UiGlobals.isSideEditorAlwaysEditMode) && UiGlobals.isSideEditorAlwaysEditMode) {
|
|
534
565
|
// special case: side editor is always edit mode
|
|
535
566
|
mode = EDITOR_MODE__EDIT;
|
|
536
567
|
} else {
|
|
537
568
|
if (secondaryIsIgnoreNextSelectionChange) {
|
|
538
|
-
mode =
|
|
569
|
+
mode = secondaryGetEditorMode();
|
|
539
570
|
if (!secondaryCanEditorViewOnly && secondaryUserCanEdit) {
|
|
540
571
|
if (secondarySelection.length > 1) {
|
|
541
572
|
if (!secondaryDisableEdit) {
|
|
@@ -581,9 +612,9 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
581
612
|
};
|
|
582
613
|
|
|
583
614
|
useEffect(() => {
|
|
584
|
-
secondarySetEditorMode(calculateEditorMode(
|
|
615
|
+
secondarySetEditorMode(calculateEditorMode());
|
|
585
616
|
|
|
586
|
-
|
|
617
|
+
secondarySetIsIgnoreNextSelectionChange(false);
|
|
587
618
|
setLastSelection(secondarySelection);
|
|
588
619
|
}, [secondarySelection]);
|
|
589
620
|
|
|
@@ -602,7 +633,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
602
633
|
// NOTE: If I don't calculate this on the fly for selection changes,
|
|
603
634
|
// we see a flash of the previous state, since useEffect hasn't yet run.
|
|
604
635
|
// (basically redo what's in the useEffect, above)
|
|
605
|
-
|
|
636
|
+
secondarySetEditorMode(calculateEditorMode());
|
|
606
637
|
}
|
|
607
638
|
|
|
608
639
|
return <WrappedComponent
|
|
@@ -615,11 +646,12 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
615
646
|
secondaryIsEditorViewOnly={secondaryIsEditorViewOnly}
|
|
616
647
|
secondaryIsAdding={secondaryIsAdding}
|
|
617
648
|
secondaryIsSaving={secondaryIsSaving}
|
|
618
|
-
secondaryEditorMode={
|
|
649
|
+
secondaryEditorMode={secondaryGetEditorMode()}
|
|
619
650
|
secondaryOnEditMode={secondarySetEditMode}
|
|
620
651
|
secondaryOnViewMode={secondarySetViewMode}
|
|
621
652
|
secondaryEditorStateRef={secondaryEditorStateRef}
|
|
622
653
|
secondarySetIsEditorShown={secondarySetIsEditorShown}
|
|
654
|
+
secondarySetIsIgnoreNextSelectionChange={secondarySetIsIgnoreNextSelectionChange}
|
|
623
655
|
secondaryOnAdd={(!secondaryUserCanEdit || secondaryDisableAdd) ? null : secondaryDoAdd}
|
|
624
656
|
secondaryOnEdit={(!secondaryUserCanEdit || secondaryDisableEdit) ? null : secondaryDoEdit}
|
|
625
657
|
secondaryOnDelete={(!secondaryUserCanEdit || secondaryDisableDelete) ? null : secondaryDoDelete}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { useState, useEffect, } from 'react';
|
|
1
|
+
import { forwardRef, useState, useEffect, useRef, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
SELECTION_MODE_SINGLE,
|
|
4
4
|
SELECTION_MODE_MULTI,
|
|
5
5
|
SELECT_UP,
|
|
6
6
|
SELECT_DOWN,
|
|
7
7
|
} from '../../../Constants/Selection.js';
|
|
8
|
+
import useForceUpdate from '../../../Hooks/useForceUpdate.js';
|
|
8
9
|
import inArray from '../../../Functions/inArray.js';
|
|
9
10
|
import _ from 'lodash';
|
|
10
11
|
|
|
11
12
|
// NOTE: This is a modified version of @onehat/ui/src/Hoc/withSelection
|
|
12
|
-
// This HOC will eventually get out of sync with that one, and may need to be updated.
|
|
13
13
|
|
|
14
14
|
export default function withSelection(WrappedComponent) {
|
|
15
|
-
return (props) => {
|
|
15
|
+
return forwardRef((props, ref) => {
|
|
16
16
|
|
|
17
17
|
if (props.secondaryDisableWithSelection) {
|
|
18
18
|
return <WrappedComponent {...props} />;
|
|
@@ -25,8 +25,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
25
25
|
return <WrappedComponent {...props} />;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const
|
|
29
|
-
{
|
|
28
|
+
const {
|
|
30
29
|
secondarySelection,
|
|
31
30
|
secondaryDefaultSelection,
|
|
32
31
|
secondaryOnChangeSelection,
|
|
@@ -49,20 +48,25 @@ export default function withSelection(WrappedComponent) {
|
|
|
49
48
|
} = props,
|
|
50
49
|
usesWithValue = !!secondarySetValue,
|
|
51
50
|
initialSelection = secondarySelection || secondaryDefaultSelection || [],
|
|
52
|
-
|
|
51
|
+
forceUpdate = useForceUpdate(),
|
|
52
|
+
secondarySelectionRef = useRef(initialSelection),
|
|
53
53
|
[isReady, setIsReady] = useState(secondarySelection || false), // if secondarySelection is already defined, or secondaryValue is not null and we don't need to load repository, it's ready
|
|
54
54
|
secondarySetSelection = (secondarySelection) => {
|
|
55
|
-
if (_.isEqual(secondarySelection,
|
|
55
|
+
if (_.isEqual(secondarySelection, secondaryGetSelection())) {
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
secondarySelectionRef.current = secondarySelection;
|
|
60
60
|
if (secondaryOnChangeSelection) {
|
|
61
61
|
secondaryOnChangeSelection(secondarySelection);
|
|
62
62
|
}
|
|
63
63
|
if (fireEvent) {
|
|
64
64
|
fireEvent('secondaryChangeSelection', secondarySelection);
|
|
65
65
|
}
|
|
66
|
+
forceUpdate();
|
|
67
|
+
},
|
|
68
|
+
secondaryGetSelection = () => {
|
|
69
|
+
return secondarySelectionRef.current;
|
|
66
70
|
},
|
|
67
71
|
secondarySelectPrev = () => {
|
|
68
72
|
secondarySelectDirection(SELECT_UP);
|
|
@@ -103,21 +107,21 @@ export default function withSelection(WrappedComponent) {
|
|
|
103
107
|
}
|
|
104
108
|
},
|
|
105
109
|
secondaryAddToSelection = (item) => {
|
|
106
|
-
const newSelection = _.clone(
|
|
110
|
+
const newSelection = _.clone(secondaryGetSelection()); // so we get a new object, so descendants rerender
|
|
107
111
|
newSelection.push(item);
|
|
108
112
|
secondarySetSelection(newSelection);
|
|
109
113
|
},
|
|
110
114
|
secondaryRemoveFromSelection = (item) => {
|
|
111
115
|
let newSelection = [];
|
|
112
116
|
if (SecondaryRepository) {
|
|
113
|
-
newSelection = _.remove(
|
|
117
|
+
newSelection = _.remove(secondaryGetSelection(), (sel) => sel !== item);
|
|
114
118
|
} else {
|
|
115
|
-
newSelection = _.remove(
|
|
119
|
+
newSelection = _.remove(secondaryGetSelection(), (sel) => sel[secondaryIdIx] !== item[secondaryIdIx]);
|
|
116
120
|
}
|
|
117
121
|
secondarySetSelection(newSelection);
|
|
118
122
|
},
|
|
119
123
|
secondaryDeselectAll = () => {
|
|
120
|
-
if (!_.isEmpty(
|
|
124
|
+
if (!_.isEmpty(secondaryGetSelection())) {
|
|
121
125
|
secondarySetSelection([]);
|
|
122
126
|
}
|
|
123
127
|
},
|
|
@@ -128,7 +132,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
128
132
|
// That way, after a load event, we'll keep the same selection, if possible.
|
|
129
133
|
const
|
|
130
134
|
newSelection = [],
|
|
131
|
-
ids = _.map(
|
|
135
|
+
ids = _.map(secondaryGetSelection(), (item) => item.id);
|
|
132
136
|
_.each(ids, (id) => {
|
|
133
137
|
const found = SecondaryRepository.getById(id);
|
|
134
138
|
if (found) {
|
|
@@ -162,9 +166,9 @@ export default function withSelection(WrappedComponent) {
|
|
|
162
166
|
secondarySelectRangeTo = (item) => {
|
|
163
167
|
// Select above max or below min to this one
|
|
164
168
|
const
|
|
165
|
-
currentSelectionLength =
|
|
169
|
+
currentSelectionLength = secondaryGetSelection().length,
|
|
166
170
|
index = getIndexOfSelectedItem(item);
|
|
167
|
-
let newSelection = _.clone(
|
|
171
|
+
let newSelection = _.clone(secondaryGetSelection()); // so we get a new object, so descendants rerender
|
|
168
172
|
|
|
169
173
|
if (currentSelectionLength) {
|
|
170
174
|
const { items, max, min, } = getMaxMinSelectionIndices();
|
|
@@ -191,10 +195,10 @@ export default function withSelection(WrappedComponent) {
|
|
|
191
195
|
},
|
|
192
196
|
secondaryIsInSelection = (item) => {
|
|
193
197
|
if (SecondaryRepository) {
|
|
194
|
-
return inArray(item,
|
|
198
|
+
return inArray(item, secondaryGetSelection());
|
|
195
199
|
}
|
|
196
200
|
|
|
197
|
-
const found = _.find(
|
|
201
|
+
const found = _.find(secondaryGetSelection(), (selectedItem) => {
|
|
198
202
|
return selectedItem[secondaryIdIx] === item[secondaryIdIx];
|
|
199
203
|
});
|
|
200
204
|
return !!found;
|
|
@@ -216,10 +220,10 @@ export default function withSelection(WrappedComponent) {
|
|
|
216
220
|
return found;
|
|
217
221
|
},
|
|
218
222
|
secondaryGetIdsFromLocalSelection = () => {
|
|
219
|
-
if (!
|
|
223
|
+
if (!secondaryGetSelection()[0]) {
|
|
220
224
|
return null;
|
|
221
225
|
}
|
|
222
|
-
const secondaryValues = _.map(
|
|
226
|
+
const secondaryValues = _.map(secondaryGetSelection(), (item) => {
|
|
223
227
|
if (SecondaryRepository) {
|
|
224
228
|
return item.id;
|
|
225
229
|
}
|
|
@@ -303,7 +307,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
303
307
|
}
|
|
304
308
|
}
|
|
305
309
|
|
|
306
|
-
if (!_.isEqual(newSelection,
|
|
310
|
+
if (!_.isEqual(newSelection, secondaryGetSelection())) {
|
|
307
311
|
secondarySetSelection(newSelection);
|
|
308
312
|
}
|
|
309
313
|
};
|
|
@@ -354,7 +358,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
354
358
|
}, [secondaryValue]);
|
|
355
359
|
|
|
356
360
|
if (self) {
|
|
357
|
-
self.secondarySelection =
|
|
361
|
+
self.secondarySelection = secondaryGetSelection();
|
|
358
362
|
self.secondarySetSelection = secondarySetSelection;
|
|
359
363
|
self.secondarySelectPrev = secondarySelectPrev;
|
|
360
364
|
self.secondarySelectNext = secondarySelectNext;
|
|
@@ -395,8 +399,10 @@ export default function withSelection(WrappedComponent) {
|
|
|
395
399
|
|
|
396
400
|
return <WrappedComponent
|
|
397
401
|
{...props}
|
|
402
|
+
ref={ref}
|
|
398
403
|
secondaryDisableWithSelection={false}
|
|
399
|
-
secondarySelection={
|
|
404
|
+
secondarySelection={secondaryGetSelection()}
|
|
405
|
+
secondaryGetSelection={secondaryGetSelection}
|
|
400
406
|
secondarySetSelection={secondarySetSelection}
|
|
401
407
|
secondarySelectionMode={secondarySelectionMode}
|
|
402
408
|
secondarySelectPrev={secondarySelectPrev}
|
|
@@ -411,5 +417,5 @@ export default function withSelection(WrappedComponent) {
|
|
|
411
417
|
secondaryGetIdsFromSelection={secondaryGetIdsFromLocalSelection}
|
|
412
418
|
secondaryGetDisplayValuesFromSelection={secondaryGetDisplayValuesFromLocalSelection}
|
|
413
419
|
/>;
|
|
414
|
-
};
|
|
420
|
+
});
|
|
415
421
|
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
EDITOR_TYPE__SIDE,
|
|
14
14
|
EDITOR_TYPE__INLINE,
|
|
15
15
|
} from '../../Constants/Editor.js';
|
|
16
|
+
import useForceUpdate from '../../Hooks/useForceUpdate.js'
|
|
16
17
|
import Button from '../Buttons/Button.js';
|
|
17
18
|
import UiGlobals from '../../UiGlobals.js';
|
|
18
19
|
import _ from 'lodash';
|
|
@@ -76,6 +77,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
76
77
|
confirm,
|
|
77
78
|
hideAlert,
|
|
78
79
|
} = props,
|
|
80
|
+
forceUpdate = useForceUpdate(),
|
|
79
81
|
listeners = useRef({}),
|
|
80
82
|
editorStateRef = useRef(),
|
|
81
83
|
newEntityDisplayValueRef = useRef(),
|
|
@@ -128,7 +130,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
128
130
|
return editorModeRef.current;
|
|
129
131
|
},
|
|
130
132
|
setEditorMode = (mode) => {
|
|
131
|
-
editorModeRef.current
|
|
133
|
+
if (editorModeRef.current !== mode) {
|
|
134
|
+
editorModeRef.current = mode;
|
|
135
|
+
forceUpdate();
|
|
136
|
+
}
|
|
132
137
|
},
|
|
133
138
|
getNewEntityDisplayValue = () => {
|
|
134
139
|
return newEntityDisplayValueRef.current;
|
|
@@ -11,7 +11,6 @@ import Form from '../Form/Form.js';
|
|
|
11
11
|
import Pdf from '../Icons/Pdf.js';
|
|
12
12
|
import UiGlobals from '../../UiGlobals.js';
|
|
13
13
|
import inArray from '../../Functions/inArray.js';
|
|
14
|
-
import testProps from '../../Functions/testProps.js';
|
|
15
14
|
import _ from 'lodash';
|
|
16
15
|
|
|
17
16
|
export default function withPdfButtons(WrappedComponent) {
|
|
@@ -23,8 +23,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
23
23
|
return <WrappedComponent {...props} ref={ref} />;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const
|
|
27
|
-
{
|
|
26
|
+
const {
|
|
28
27
|
selection,
|
|
29
28
|
defaultSelection,
|
|
30
29
|
onChangeSelection,
|
|
@@ -120,7 +119,9 @@ export default function withSelection(WrappedComponent) {
|
|
|
120
119
|
setSelection(newSelection);
|
|
121
120
|
},
|
|
122
121
|
deselectAll = () => {
|
|
123
|
-
|
|
122
|
+
if (!_.isEmpty(getSelection())) {
|
|
123
|
+
setSelection([]);
|
|
124
|
+
}
|
|
124
125
|
},
|
|
125
126
|
refreshSelection = () => {
|
|
126
127
|
// When Repository reloads, the entities get destroyed.
|
|
@@ -39,11 +39,21 @@ export function verifyElementDoesNotExist(selectors) {
|
|
|
39
39
|
cy.get('body').then(($body) => {
|
|
40
40
|
const selectorString = getTestIdSelectors(selectors);
|
|
41
41
|
if ($body.find(selectorString).length > 0) {
|
|
42
|
-
throw new Error(`Element with selectors ${selectorString} exists in the DOM`);
|
|
42
|
+
throw new Error(`Element with selectors ${selectorString} exists in the DOM, when it should not`);
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Verifies that an element with the given selectors exists in the DOM.
|
|
49
|
+
* @argument {string | string[]} selectors - data-testid attribute values
|
|
50
|
+
* If an array is given, these will be considered nested selectors.
|
|
51
|
+
* e.g. ['parent', 'child'] will be converted to '[data-testid="parent"] [data-testid="child"]'
|
|
52
|
+
*/
|
|
53
|
+
export function verifyElementExists(selectors) {
|
|
54
|
+
cy.get(getTestIdSelectors(selectors)).should('exist');
|
|
55
|
+
}
|
|
56
|
+
|
|
47
57
|
/**
|
|
48
58
|
* Builds selector string for data-testid attributes.
|
|
49
59
|
* It leaves classname, id, and attribute selectors unchanged.
|
|
@@ -103,13 +113,21 @@ export function drag(draggableSelectors, droppableSelectors, options = {}) {
|
|
|
103
113
|
});
|
|
104
114
|
}
|
|
105
115
|
|
|
116
|
+
|
|
117
|
+
// conditions functions that make use of cypress-if
|
|
106
118
|
export function ifExists(parentSelectors, name, cb) {
|
|
107
119
|
if (_.isString(parentSelectors)) {
|
|
108
120
|
parentSelectors = [parentSelectors];
|
|
109
121
|
}
|
|
110
|
-
return getDomNode([...parentSelectors, name]).if().then((node) => {
|
|
122
|
+
return getDomNode([...parentSelectors, name]).if().then((node) => {
|
|
111
123
|
if (node) {
|
|
112
124
|
cb(node);
|
|
113
125
|
}
|
|
114
126
|
});
|
|
115
127
|
}
|
|
128
|
+
export function ifNotExists(parentSelectors, name, cb) {
|
|
129
|
+
if (_.isString(parentSelectors)) {
|
|
130
|
+
parentSelectors = [parentSelectors];
|
|
131
|
+
}
|
|
132
|
+
return getDomNode([...parentSelectors, name]).if('not.exist').then(cb);
|
|
133
|
+
}
|
|
@@ -274,100 +274,100 @@ export function setInputValue(selectors, value) {
|
|
|
274
274
|
setTextValue(selectors, value);
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Given a form,
|
|
279
|
+
* return a url-encoded string representing all keys and values
|
|
280
|
+
*
|
|
281
|
+
* @param {jQuery} form
|
|
282
|
+
* @return {String}
|
|
283
|
+
* /
|
|
284
|
+
export function formSerialize(form) {
|
|
285
|
+
'use strict';
|
|
286
|
+
var i, j, len, jLen, formElement,
|
|
287
|
+
q = [],
|
|
288
|
+
theForm = form[0],
|
|
289
|
+
varCounters = {};
|
|
290
|
+
|
|
291
|
+
function addNameValue(name, value) { // create this function so I can use varCounters for
|
|
292
|
+
var matches = name.match(/([\w\d]+)\[\]/i),
|
|
293
|
+
varName,
|
|
294
|
+
ix = 0;
|
|
295
|
+
if (matches && matches[1]) {
|
|
296
|
+
varName = matches[1];
|
|
297
|
+
if (typeof varCounters[varName] === 'undefined') {
|
|
298
|
+
varCounters[varName] = ix;
|
|
299
|
+
} else {
|
|
300
|
+
ix = ++varCounters[varName];
|
|
301
|
+
}
|
|
302
|
+
name = varName + '[' + ix + ']';
|
|
303
|
+
}
|
|
304
|
+
q.push(urlencode(name) + '=' + urlencode(value));
|
|
305
|
+
}
|
|
277
306
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
//
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
//
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
// addNameValue(formElement.name, formElement.value);
|
|
343
|
-
// break;
|
|
344
|
-
// case 'select':
|
|
345
|
-
// switch (formElement.type) {
|
|
346
|
-
// case 'select-one':
|
|
347
|
-
// addNameValue(formElement.name, formElement.value);
|
|
348
|
-
// break;
|
|
349
|
-
// case 'select-multiple':
|
|
350
|
-
// for (j = 0, jLen = formElement.options.length; j < jLen; j++) {
|
|
351
|
-
// if (formElement.options[j].selected) {
|
|
352
|
-
// addNameValue(formElement.name, formElement.options[j].value);
|
|
353
|
-
// }
|
|
354
|
-
// }
|
|
355
|
-
// break;
|
|
356
|
-
// }
|
|
357
|
-
// break;
|
|
358
|
-
// case 'button': // jQuery does not submit these, though it is an HTML4 successful control
|
|
359
|
-
// switch (formElement.type) {
|
|
360
|
-
// case 'reset':
|
|
361
|
-
// case 'submit':
|
|
362
|
-
// case 'button':
|
|
363
|
-
// addNameValue(formElement.name, formElement.value);
|
|
364
|
-
// break;
|
|
365
|
-
// }
|
|
366
|
-
// break;
|
|
367
|
-
// }
|
|
368
|
-
// }
|
|
369
|
-
// return q.join('&');
|
|
370
|
-
// }
|
|
307
|
+
if (!theForm || !theForm.nodeName || theForm.nodeName.toLowerCase() !== 'form') {
|
|
308
|
+
throw 'You must supply a form element';
|
|
309
|
+
}
|
|
310
|
+
for (i = 0, len = theForm.elements.length; i < len; i++) {
|
|
311
|
+
formElement = theForm.elements[i];
|
|
312
|
+
if (formElement.name === '' || formElement.disabled) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
switch (formElement.nodeName.toLowerCase()) {
|
|
316
|
+
case 'input':
|
|
317
|
+
switch (formElement.type) {
|
|
318
|
+
case 'text':
|
|
319
|
+
case 'hidden':
|
|
320
|
+
case 'password':
|
|
321
|
+
case 'button': // Not submitted when submitting form manually, though jQuery does serialize this and it can be an HTML4 successful control
|
|
322
|
+
case 'submit':
|
|
323
|
+
addNameValue(formElement.name, formElement.value);
|
|
324
|
+
break;
|
|
325
|
+
case 'checkbox':
|
|
326
|
+
case 'radio':
|
|
327
|
+
if (formElement.checked) {
|
|
328
|
+
addNameValue(formElement.name, formElement.value);
|
|
329
|
+
} else if (formElement.value === '1') {
|
|
330
|
+
addNameValue(formElement.name, '0'); // Submit actual value of zero for booleans, instead of no value at all
|
|
331
|
+
}
|
|
332
|
+
break;
|
|
333
|
+
case 'file':
|
|
334
|
+
// addNameValue(formElement.name, formElement.value); // Will work and part of HTML4 "successful controls", but not used in jQuery
|
|
335
|
+
break;
|
|
336
|
+
case 'reset':
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
break;
|
|
340
|
+
case 'textarea':
|
|
341
|
+
addNameValue(formElement.name, formElement.value);
|
|
342
|
+
break;
|
|
343
|
+
case 'select':
|
|
344
|
+
switch (formElement.type) {
|
|
345
|
+
case 'select-one':
|
|
346
|
+
addNameValue(formElement.name, formElement.value);
|
|
347
|
+
break;
|
|
348
|
+
case 'select-multiple':
|
|
349
|
+
for (j = 0, jLen = formElement.options.length; j < jLen; j++) {
|
|
350
|
+
if (formElement.options[j].selected) {
|
|
351
|
+
addNameValue(formElement.name, formElement.options[j].value);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
break;
|
|
357
|
+
case 'button': // jQuery does not submit these, though it is an HTML4 successful control
|
|
358
|
+
switch (formElement.type) {
|
|
359
|
+
case 'reset':
|
|
360
|
+
case 'submit':
|
|
361
|
+
case 'button':
|
|
362
|
+
addNameValue(formElement.name, formElement.value);
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return q.join('&');
|
|
369
|
+
}
|
|
370
|
+
*/
|
|
371
371
|
|
|
372
372
|
|
|
373
373
|
|
|
@@ -377,94 +377,100 @@ export function setInputValue(selectors, value) {
|
|
|
377
377
|
// / /_/ / __/ /_/ /_/ __/ / (__ )
|
|
378
378
|
// \____/\___/\__/\__/\___/_/ /____/
|
|
379
379
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
380
|
+
/**
|
|
381
|
+
* Get data from a form
|
|
382
|
+
* @param {object} schema - fieldName/fieldType pairs
|
|
383
|
+
* @returns {object} formValues - object of fieldName/value pairs
|
|
384
|
+
* /
|
|
385
|
+
export function getFormValues(editor, schema) {
|
|
386
|
+
const fields = editor.find('.x-form-field'),
|
|
387
|
+
formValues = {};
|
|
388
|
+
|
|
389
|
+
_.each(fields, (field) => {
|
|
390
|
+
const fieldType = schema[field.name];
|
|
391
|
+
switch(fieldType) {
|
|
392
|
+
case 'checkbox':
|
|
393
|
+
formValues[fieldName] = getCheckboxValue(fieldName);
|
|
394
|
+
break;
|
|
395
|
+
case 'combo':
|
|
396
|
+
formValues[fieldName] = getComboValue(fieldName);
|
|
397
|
+
break;
|
|
398
|
+
case 'date':
|
|
399
|
+
formValues[fieldName] = getDateValue(fieldName);
|
|
400
|
+
break;
|
|
401
|
+
case 'datetime':
|
|
402
|
+
formValues[fieldName] = getDatetimeValue(fieldName);
|
|
403
|
+
break;
|
|
404
|
+
case 'file':
|
|
405
|
+
formValues[fieldName] = getFileValue(fieldName);
|
|
406
|
+
break;
|
|
407
|
+
case 'number':
|
|
408
|
+
formValues[fieldName] = getNumberValue(fieldName);
|
|
409
|
+
break;
|
|
410
|
+
case 'radio':
|
|
411
|
+
formValues[fieldName] = getRadioValue(fieldName);
|
|
412
|
+
break;
|
|
413
|
+
case 'tag':
|
|
414
|
+
formValues[fieldName] = getTagValue(fieldName);
|
|
415
|
+
break;
|
|
416
|
+
case 'text':
|
|
417
|
+
case 'textarea':
|
|
418
|
+
formValues[fieldName] = getTextValue(fieldName);
|
|
419
|
+
break;
|
|
420
|
+
case 'time':
|
|
421
|
+
formValues[fieldName] = getTimeValue(fieldName);
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
return formValues;
|
|
426
|
+
}
|
|
427
|
+
*/
|
|
427
428
|
|
|
428
429
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
//
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
430
|
+
/**
|
|
431
|
+
* Validate that form values match what they're supposed to
|
|
432
|
+
* /
|
|
433
|
+
export function validateFormValues(data, schema) {
|
|
434
|
+
cy.wrap().then(() => { // Wrap this in a Cypress promise, so it executes in correct order, relative to other Cypress promises
|
|
435
|
+
|
|
436
|
+
const formValues = getFormValues(schema);
|
|
437
|
+
let diff = deepDiffObj(formValues, data);
|
|
438
|
+
|
|
439
|
+
// SPECIAL CASE: Omit password fields from diff
|
|
440
|
+
const omitFields = [];
|
|
441
|
+
_.each(diff, (value, key) => {
|
|
442
|
+
if (key.match(/^password/i)) {
|
|
443
|
+
omitFields.push(key);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
if (omitFields.length) {
|
|
447
|
+
diff = _.omit(diff, omitFields);
|
|
448
|
+
}
|
|
448
449
|
|
|
449
|
-
//
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
450
|
+
// If there are still any differences, log them
|
|
451
|
+
if (_.keys(diff).length > 0) {
|
|
452
|
+
console.log('data', data);
|
|
453
|
+
console.log('formValues', formValues);
|
|
454
|
+
console.log('diff', diff);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
expect(diff).to.deep.equal({});
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
*/
|
|
459
461
|
|
|
460
462
|
|
|
461
463
|
|
|
462
464
|
// export function getCheckboxValue(fieldName) {
|
|
463
465
|
|
|
464
466
|
// }
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
467
|
+
export function getComboValue(selectors, fieldName) {
|
|
468
|
+
cy.log('getComboValue ' + fieldName);
|
|
469
|
+
return getDomNode([...selectors, 'field-' + fieldName, 'input']).then((field) => {
|
|
470
|
+
return cy.wrap(field)
|
|
471
|
+
.invoke('val');
|
|
472
|
+
});
|
|
473
|
+
}
|
|
468
474
|
// export function getDateValue(fieldName) {
|
|
469
475
|
|
|
470
476
|
// }
|
|
@@ -11,6 +11,15 @@ import _ from 'lodash';
|
|
|
11
11
|
const $ = Cypress.$;
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
// Get cells
|
|
15
|
+
export function getGridCellValue(gridSelector, rowId, field) {
|
|
16
|
+
cy.log('getGridCellValue ' + gridSelector + ' ' + rowId + ' ' + field);
|
|
17
|
+
const rowSelector = getGridRowSelectorById(gridSelector, rowId);
|
|
18
|
+
return getDomNode([gridSelector, rowSelector, 'cell-' + field])
|
|
19
|
+
.invoke('text');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
14
23
|
|
|
15
24
|
// Get rows
|
|
16
25
|
export function hasRowWithFieldValue(gridSelector, field, value) {
|
|
@@ -56,12 +65,16 @@ export function selectGridRowIfNotAlreadySelectedById(gridSelector, id) {
|
|
|
56
65
|
}
|
|
57
66
|
})
|
|
58
67
|
}
|
|
68
|
+
export function selectGridRowByIx(gridSelector, ix) {
|
|
69
|
+
cy.log('selectGridRowByIx ' + gridSelector + ' ' + ix);
|
|
70
|
+
|
|
71
|
+
ix++; // compensate for header row
|
|
72
|
+
getDomNode([gridSelector, '[data-ix=' + ix + ']'])
|
|
73
|
+
.click();
|
|
74
|
+
}
|
|
59
75
|
// export function selectRowWithText(grid, text) {
|
|
60
76
|
// getRowWithText(grid, text).click(5, 5);
|
|
61
77
|
// }
|
|
62
|
-
// export function selectRowWithIx(grid, ix) {
|
|
63
|
-
// getRowWithIx(grid, ix).click(5, 5);
|
|
64
|
-
// }
|
|
65
78
|
// export function cmdClickRowWithId(grid, id) {
|
|
66
79
|
// getRowWithId(grid, id).click('left', { metaKey: true });
|
|
67
80
|
// }
|
|
@@ -234,12 +247,8 @@ export function getModelFromGridSelector(gridSelector) {
|
|
|
234
247
|
}
|
|
235
248
|
export function getGridRowSelectorById(gridSelector, id) {
|
|
236
249
|
const
|
|
237
|
-
model = getModelFromGridSelector(gridSelector)
|
|
238
|
-
|
|
239
|
-
if (!model) {
|
|
240
|
-
debugger;
|
|
241
|
-
}
|
|
242
|
-
const inflected = fixInflector(Inflector.camelize(Inflector.pluralize(model)));
|
|
250
|
+
model = getModelFromGridSelector(gridSelector),
|
|
251
|
+
inflected = fixInflector(Inflector.camelize(Inflector.pluralize(model)));
|
|
243
252
|
return inflected + '-' + id;
|
|
244
253
|
}
|
|
245
254
|
|