@onehat/ui 0.4.33 → 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 +4 -2
- package/src/Components/Form/Form.js +11 -16
- 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 +48 -15
- package/src/Components/Hoc/withPdfButtons.js +4 -0
- package/src/Components/Hoc/withSelection.js +24 -19
- package/src/Functions/Cypress/button_functions.js +8 -8
- package/src/Functions/Cypress/crud_functions.js +52 -3
- package/src/Functions/Cypress/dom_functions.js +53 -2
- package/src/Functions/Cypress/form_functions.js +179 -173
- package/src/Functions/Cypress/grid_functions.js +18 -9
- package/src/Functions/Cypress/utilities.js +8 -1
|
@@ -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';
|
|
@@ -24,7 +25,6 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
24
25
|
return <WrappedComponent {...props} ref={ref} isTree={isTree} />;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
let [editorMode, setEditorMode] = useState(EDITOR_MODE__VIEW); // Can change below, so use 'let'
|
|
28
28
|
const {
|
|
29
29
|
userCanEdit = true, // not permissions, but capability
|
|
30
30
|
userCanView = true,
|
|
@@ -69,6 +69,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
69
69
|
|
|
70
70
|
// withSelection
|
|
71
71
|
selection,
|
|
72
|
+
getSelection,
|
|
72
73
|
setSelection,
|
|
73
74
|
|
|
74
75
|
// withAlert
|
|
@@ -76,16 +77,24 @@ 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(),
|
|
84
|
+
editorModeRef = useRef(EDITOR_MODE__VIEW),
|
|
85
|
+
isIgnoreNextSelectionChangeRef = useRef(false),
|
|
82
86
|
[currentRecord, setCurrentRecord] = useState(null),
|
|
83
87
|
[isAdding, setIsAdding] = useState(false),
|
|
84
88
|
[isSaving, setIsSaving] = useState(false),
|
|
85
89
|
[isEditorShown, setIsEditorShownRaw] = useState(false),
|
|
86
90
|
[isEditorViewOnly, setIsEditorViewOnly] = useState(canEditorViewOnly), // current state of whether editor is in view-only mode
|
|
87
|
-
[isIgnoreNextSelectionChange, setIsIgnoreNextSelectionChange] = useState(false),
|
|
88
91
|
[lastSelection, setLastSelection] = useState(),
|
|
92
|
+
setIsIgnoreNextSelectionChange = (bool) => {
|
|
93
|
+
isIgnoreNextSelectionChangeRef.current = bool;
|
|
94
|
+
},
|
|
95
|
+
getIsIgnoreNextSelectionChange = () => {
|
|
96
|
+
return isIgnoreNextSelectionChangeRef.current;
|
|
97
|
+
},
|
|
89
98
|
setIsEditorShown = (bool) => {
|
|
90
99
|
setIsEditorShownRaw(bool);
|
|
91
100
|
if (!bool && onEditorClose) {
|
|
@@ -96,8 +105,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
96
105
|
function doIt() {
|
|
97
106
|
setSelection(newSelection);
|
|
98
107
|
}
|
|
99
|
-
const
|
|
100
|
-
|
|
108
|
+
const
|
|
109
|
+
formState = editorStateRef.current,
|
|
110
|
+
selection = getSelection();
|
|
111
|
+
if (!_.isEmpty(formState?.dirtyFields) && newSelection !== selection && getEditorMode() === EDITOR_MODE__EDIT) {
|
|
101
112
|
confirm('This record has unsaved changes. Are you sure you want to cancel editing? Changes will be lost.', doIt);
|
|
102
113
|
} else if (selection && selection[0] && !selection[0].isDestroyed && (selection[0]?.isPhantom || selection[0]?.isRemotePhantom)) {
|
|
103
114
|
confirm('This new record is unsaved. Are you sure you want to cancel editing? Changes will be lost.', async () => {
|
|
@@ -115,6 +126,15 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
115
126
|
listeners.current = obj;
|
|
116
127
|
// forceUpdate(); // we don't want to get into an infinite loop of renders. Simply directly assign the listeners in every child render
|
|
117
128
|
},
|
|
129
|
+
getEditorMode = () => {
|
|
130
|
+
return editorModeRef.current;
|
|
131
|
+
},
|
|
132
|
+
setEditorMode = (mode) => {
|
|
133
|
+
if (editorModeRef.current !== mode) {
|
|
134
|
+
editorModeRef.current = mode;
|
|
135
|
+
forceUpdate();
|
|
136
|
+
}
|
|
137
|
+
},
|
|
118
138
|
getNewEntityDisplayValue = () => {
|
|
119
139
|
return newEntityDisplayValueRef.current;
|
|
120
140
|
},
|
|
@@ -124,6 +144,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
124
144
|
return;
|
|
125
145
|
}
|
|
126
146
|
|
|
147
|
+
const selection = getSelection();
|
|
127
148
|
let addValues = values;
|
|
128
149
|
|
|
129
150
|
if (Repository?.isLoading) {
|
|
@@ -221,6 +242,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
221
242
|
showPermissionsError(EDIT);
|
|
222
243
|
return;
|
|
223
244
|
}
|
|
245
|
+
const selection = getSelection();
|
|
224
246
|
if (_.isEmpty(selection) || (_.isArray(selection) && (selection.length > 1 || selection[0]?.isDestroyed))) {
|
|
225
247
|
return;
|
|
226
248
|
}
|
|
@@ -243,6 +265,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
243
265
|
if (_.isFunction(args)) {
|
|
244
266
|
cb = args;
|
|
245
267
|
}
|
|
268
|
+
const selection = getSelection();
|
|
246
269
|
if (_.isEmpty(selection) || (_.isArray(selection) && (selection.length > 1 || selection[0]?.isDestroyed))) {
|
|
247
270
|
return;
|
|
248
271
|
}
|
|
@@ -301,6 +324,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
301
324
|
showPermissionsError(DELETE);
|
|
302
325
|
return;
|
|
303
326
|
}
|
|
327
|
+
const selection = getSelection();
|
|
304
328
|
if (getListeners().onBeforeDelete) {
|
|
305
329
|
const listenerResult = await getListeners().onBeforeDelete(selection);
|
|
306
330
|
if (listenerResult === false) {
|
|
@@ -341,6 +365,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
341
365
|
|
|
342
366
|
// check permissions for view
|
|
343
367
|
|
|
368
|
+
const selection = getSelection();
|
|
344
369
|
if (selection.length !== 1) {
|
|
345
370
|
return;
|
|
346
371
|
}
|
|
@@ -363,7 +388,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
363
388
|
|
|
364
389
|
// check permissions for duplicate
|
|
365
390
|
|
|
366
|
-
|
|
391
|
+
const selection = getSelection();
|
|
367
392
|
if (selection.length !== 1) {
|
|
368
393
|
return;
|
|
369
394
|
}
|
|
@@ -384,6 +409,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
384
409
|
},
|
|
385
410
|
onRemoteDuplicate = async () => {
|
|
386
411
|
const
|
|
412
|
+
selection = getSelection(),
|
|
387
413
|
entity = selection[0],
|
|
388
414
|
duplicateEntity = await Repository.remoteDuplicate(entity);
|
|
389
415
|
|
|
@@ -392,14 +418,16 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
392
418
|
doEdit();
|
|
393
419
|
},
|
|
394
420
|
doEditorSave = async (data, e) => {
|
|
395
|
-
let mode =
|
|
421
|
+
let mode = getEditorMode() === EDITOR_MODE__ADD ? ADD : EDIT;
|
|
396
422
|
if (canUser && !canUser(mode)) {
|
|
397
423
|
showPermissionsError(mode);
|
|
398
424
|
return;
|
|
399
425
|
}
|
|
400
426
|
|
|
401
427
|
// NOTE: The Form submits onSave for both adds (when not isAutoSsave) and edits.
|
|
402
|
-
const
|
|
428
|
+
const
|
|
429
|
+
selection = getSelection(),
|
|
430
|
+
isSingle = selection.length === 1;
|
|
403
431
|
let useStaged = false;
|
|
404
432
|
if (isSingle) {
|
|
405
433
|
// just update this one entity
|
|
@@ -446,7 +474,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
446
474
|
if (onChange) {
|
|
447
475
|
onChange(selection);
|
|
448
476
|
}
|
|
449
|
-
if (
|
|
477
|
+
if (getEditorMode() === EDITOR_MODE__ADD) {
|
|
450
478
|
if (onAdd) {
|
|
451
479
|
await onAdd(selection);
|
|
452
480
|
}
|
|
@@ -459,7 +487,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
459
487
|
} else {
|
|
460
488
|
setEditorMode(EDITOR_MODE__VIEW);
|
|
461
489
|
}
|
|
462
|
-
} else if (
|
|
490
|
+
} else if (getEditorMode() === EDITOR_MODE__EDIT) {
|
|
463
491
|
if (getListeners().onAfterEdit) {
|
|
464
492
|
await getListeners().onAfterEdit(selection);
|
|
465
493
|
}
|
|
@@ -475,6 +503,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
475
503
|
doEditorCancel = () => {
|
|
476
504
|
async function doIt() {
|
|
477
505
|
const
|
|
506
|
+
selection = getSelection(),
|
|
478
507
|
isSingle = selection.length === 1,
|
|
479
508
|
isPhantom = selection[0] && !selection[0]?.isDestroyed && selection[0].isPhantom;
|
|
480
509
|
if (isSingle && isPhantom) {
|
|
@@ -513,9 +542,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
513
542
|
setIsEditorShown(false);
|
|
514
543
|
});
|
|
515
544
|
},
|
|
516
|
-
calculateEditorMode = (
|
|
545
|
+
calculateEditorMode = () => {
|
|
517
546
|
|
|
518
|
-
let
|
|
547
|
+
let isIgnoreNextSelectionChange = getIsIgnoreNextSelectionChange(),
|
|
548
|
+
doStayInEditModeOnSelectionChange = stayInEditModeOnSelectionChange;
|
|
519
549
|
if (!_.isNil(UiGlobals.stayInEditModeOnSelectionChange)) {
|
|
520
550
|
// allow global override to for this property
|
|
521
551
|
doStayInEditModeOnSelectionChange = UiGlobals.stayInEditModeOnSelectionChange;
|
|
@@ -525,13 +555,14 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
525
555
|
}
|
|
526
556
|
|
|
527
557
|
// calculateEditorMode gets called only on selection changes
|
|
558
|
+
const selection = getSelection();
|
|
528
559
|
let mode;
|
|
529
560
|
if (editorType === EDITOR_TYPE__SIDE && !_.isNil(UiGlobals.isSideEditorAlwaysEditMode) && UiGlobals.isSideEditorAlwaysEditMode) {
|
|
530
561
|
// special case: side editor is always edit mode
|
|
531
562
|
mode = EDITOR_MODE__EDIT;
|
|
532
563
|
} else {
|
|
533
564
|
if (isIgnoreNextSelectionChange) {
|
|
534
|
-
mode =
|
|
565
|
+
mode = getEditorMode();
|
|
535
566
|
if (!canEditorViewOnly && userCanEdit) {
|
|
536
567
|
if (selection.length > 1) {
|
|
537
568
|
if (!disableEdit) {
|
|
@@ -577,7 +608,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
577
608
|
};
|
|
578
609
|
|
|
579
610
|
useEffect(() => {
|
|
580
|
-
setEditorMode(calculateEditorMode(
|
|
611
|
+
setEditorMode(calculateEditorMode());
|
|
581
612
|
|
|
582
613
|
setIsIgnoreNextSelectionChange(false);
|
|
583
614
|
setLastSelection(selection);
|
|
@@ -598,7 +629,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
598
629
|
// NOTE: If I don't calculate this on the fly for selection changes,
|
|
599
630
|
// we see a flash of the previous state, since useEffect hasn't yet run.
|
|
600
631
|
// (basically redo what's in the useEffect, above)
|
|
601
|
-
|
|
632
|
+
setEditorMode(calculateEditorMode());
|
|
602
633
|
}
|
|
603
634
|
|
|
604
635
|
return <WrappedComponent
|
|
@@ -611,11 +642,13 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
611
642
|
isEditorViewOnly={isEditorViewOnly}
|
|
612
643
|
isAdding={isAdding}
|
|
613
644
|
isSaving={isSaving}
|
|
614
|
-
editorMode={
|
|
645
|
+
editorMode={getEditorMode()}
|
|
646
|
+
getEditorMode={getEditorMode}
|
|
615
647
|
onEditMode={setEditMode}
|
|
616
648
|
onViewMode={setViewMode}
|
|
617
649
|
editorStateRef={editorStateRef}
|
|
618
650
|
setIsEditorShown={setIsEditorShown}
|
|
651
|
+
setIsIgnoreNextSelectionChange={setIsIgnoreNextSelectionChange}
|
|
619
652
|
onAdd={(!userCanEdit || disableAdd) ? null : doAdd}
|
|
620
653
|
onEdit={(!userCanEdit || disableEdit) ? null : doEdit}
|
|
621
654
|
onDelete={(!userCanEdit || disableDelete) ? null : doDelete}
|
|
@@ -206,6 +206,8 @@ export default function withPdfButtons(WrappedComponent) {
|
|
|
206
206
|
h: 800,
|
|
207
207
|
w: styles.FORM_STACK_ROW_THRESHOLD + 10,
|
|
208
208
|
body: <Form
|
|
209
|
+
parent={self}
|
|
210
|
+
reference="chooseFieldsForm"
|
|
209
211
|
editorType={EDITOR_TYPE__PLAIN}
|
|
210
212
|
alert={alert}
|
|
211
213
|
columnDefaults={{
|
|
@@ -244,6 +246,8 @@ export default function withPdfButtons(WrappedComponent) {
|
|
|
244
246
|
w: 510, // 510 so it's over the stack threshold
|
|
245
247
|
h: 500,
|
|
246
248
|
body: <Form
|
|
249
|
+
parent={self}
|
|
250
|
+
reference="chooseEmailAddressForm"
|
|
247
251
|
submitBtnLabel='Email PDF'
|
|
248
252
|
onSubmit={(values)=> {
|
|
249
253
|
hideModal();
|
|
@@ -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,
|
|
@@ -48,14 +47,14 @@ export default function withSelection(WrappedComponent) {
|
|
|
48
47
|
usesWithValue = !!setValue,
|
|
49
48
|
initialSelection = selection || defaultSelection || [],
|
|
50
49
|
forceUpdate = useForceUpdate(),
|
|
51
|
-
|
|
50
|
+
selectionRef = useRef(initialSelection),
|
|
52
51
|
[isReady, setIsReady] = useState(selection || false), // if selection is already defined, or value is not null and we don't need to load repository, it's ready
|
|
53
52
|
setSelection = (selection) => {
|
|
54
|
-
if (_.isEqual(selection,
|
|
53
|
+
if (_.isEqual(selection, getSelection())) {
|
|
55
54
|
return;
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
|
|
57
|
+
selectionRef.current = selection;
|
|
59
58
|
if (onChangeSelection) {
|
|
60
59
|
onChangeSelection(selection);
|
|
61
60
|
}
|
|
@@ -64,6 +63,9 @@ export default function withSelection(WrappedComponent) {
|
|
|
64
63
|
}
|
|
65
64
|
forceUpdate();
|
|
66
65
|
},
|
|
66
|
+
getSelection = () => {
|
|
67
|
+
return selectionRef.current;
|
|
68
|
+
}
|
|
67
69
|
selectPrev = () => {
|
|
68
70
|
selectDirection(SELECT_UP);
|
|
69
71
|
},
|
|
@@ -103,21 +105,23 @@ export default function withSelection(WrappedComponent) {
|
|
|
103
105
|
}
|
|
104
106
|
},
|
|
105
107
|
addToSelection = (item) => {
|
|
106
|
-
const newSelection = _.clone(
|
|
108
|
+
const newSelection = _.clone(getSelection()); // so we get a new object, so descendants rerender
|
|
107
109
|
newSelection.push(item);
|
|
108
110
|
setSelection(newSelection);
|
|
109
111
|
},
|
|
110
112
|
removeFromSelection = (item) => {
|
|
111
113
|
let newSelection = [];
|
|
112
114
|
if (Repository) {
|
|
113
|
-
newSelection = _.remove(
|
|
115
|
+
newSelection = _.remove(getSelection(), (sel) => sel !== item);
|
|
114
116
|
} else {
|
|
115
|
-
newSelection = _.remove(
|
|
117
|
+
newSelection = _.remove(getSelection(), (sel) => sel[idIx] !== item[idIx]);
|
|
116
118
|
}
|
|
117
119
|
setSelection(newSelection);
|
|
118
120
|
},
|
|
119
121
|
deselectAll = () => {
|
|
120
|
-
|
|
122
|
+
if (!_.isEmpty(getSelection())) {
|
|
123
|
+
setSelection([]);
|
|
124
|
+
}
|
|
121
125
|
},
|
|
122
126
|
refreshSelection = () => {
|
|
123
127
|
// When Repository reloads, the entities get destroyed.
|
|
@@ -126,7 +130,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
126
130
|
// That way, after a load event, we'll keep the same selection, if possible.
|
|
127
131
|
const
|
|
128
132
|
newSelection = [],
|
|
129
|
-
ids = _.map(
|
|
133
|
+
ids = _.map(getSelection(), (item) => item.id);
|
|
130
134
|
_.each(ids, (id) => {
|
|
131
135
|
const found = Repository.getById(id);
|
|
132
136
|
if (found) {
|
|
@@ -160,9 +164,9 @@ export default function withSelection(WrappedComponent) {
|
|
|
160
164
|
selectRangeTo = (item) => {
|
|
161
165
|
// Select above max or below min to this one
|
|
162
166
|
const
|
|
163
|
-
currentSelectionLength =
|
|
167
|
+
currentSelectionLength = getSelection().length,
|
|
164
168
|
index = getIndexOfSelectedItem(item);
|
|
165
|
-
let newSelection = _.clone(
|
|
169
|
+
let newSelection = _.clone(getSelection()); // so we get a new object, so descendants rerender
|
|
166
170
|
|
|
167
171
|
if (currentSelectionLength) {
|
|
168
172
|
const { items, max, min, } = getMaxMinSelectionIndices();
|
|
@@ -189,10 +193,10 @@ export default function withSelection(WrappedComponent) {
|
|
|
189
193
|
},
|
|
190
194
|
isInSelection = (item) => {
|
|
191
195
|
if (Repository) {
|
|
192
|
-
return inArray(item,
|
|
196
|
+
return inArray(item, getSelection());
|
|
193
197
|
}
|
|
194
198
|
|
|
195
|
-
const found = _.find(
|
|
199
|
+
const found = _.find(getSelection(), (selectedItem) => {
|
|
196
200
|
return selectedItem[idIx] === item[idIx];
|
|
197
201
|
});
|
|
198
202
|
return !!found;
|
|
@@ -214,10 +218,10 @@ export default function withSelection(WrappedComponent) {
|
|
|
214
218
|
return found;
|
|
215
219
|
},
|
|
216
220
|
getIdsFromLocalSelection = () => {
|
|
217
|
-
if (!
|
|
221
|
+
if (!getSelection()[0]) {
|
|
218
222
|
return null;
|
|
219
223
|
}
|
|
220
|
-
const values = _.map(
|
|
224
|
+
const values = _.map(getSelection(), (item) => {
|
|
221
225
|
if (Repository) {
|
|
222
226
|
return item.id;
|
|
223
227
|
}
|
|
@@ -301,7 +305,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
301
305
|
}
|
|
302
306
|
}
|
|
303
307
|
|
|
304
|
-
if (!_.isEqual(newSelection,
|
|
308
|
+
if (!_.isEqual(newSelection, getSelection())) {
|
|
305
309
|
setSelection(newSelection);
|
|
306
310
|
}
|
|
307
311
|
};
|
|
@@ -352,7 +356,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
352
356
|
}, [value]);
|
|
353
357
|
|
|
354
358
|
if (self) {
|
|
355
|
-
self.selection =
|
|
359
|
+
self.selection = getSelection();
|
|
356
360
|
self.setSelection = setSelection;
|
|
357
361
|
self.selectPrev = selectPrev;
|
|
358
362
|
self.selectNext = selectNext;
|
|
@@ -395,7 +399,8 @@ export default function withSelection(WrappedComponent) {
|
|
|
395
399
|
{...props}
|
|
396
400
|
ref={ref}
|
|
397
401
|
disableWithSelection={false}
|
|
398
|
-
selection={
|
|
402
|
+
selection={getSelection()}
|
|
403
|
+
getSelection={getSelection}
|
|
399
404
|
setSelection={setSelection}
|
|
400
405
|
selectionMode={selectionMode}
|
|
401
406
|
selectPrev={selectPrev}
|
|
@@ -61,7 +61,7 @@ export function clickXButton(parentSelectors) {
|
|
|
61
61
|
}
|
|
62
62
|
export function clickXButtonIfEnabled(parentSelectors) {
|
|
63
63
|
cy.log('clickXButtonIfEnabled');
|
|
64
|
-
return clickButtonIfEnabled(parentSelectors, 'xBtn'
|
|
64
|
+
return clickButtonIfEnabled(parentSelectors, 'xBtn');
|
|
65
65
|
}
|
|
66
66
|
export function clickTrigger(parentSelectors) {
|
|
67
67
|
cy.log('clickTrigger');
|
|
@@ -91,29 +91,29 @@ export function toSideMode(parentSelectors) {
|
|
|
91
91
|
cy.log('toSideMode');
|
|
92
92
|
return clickButtonIfEnabled(parentSelectors, 'sideModeBtn');
|
|
93
93
|
}
|
|
94
|
-
export function clickButton(parentSelectors, name) { // requires the button to be enabled
|
|
94
|
+
export function clickButton(parentSelectors, name, options) { // requires the button to be enabled
|
|
95
95
|
if (_.isString(parentSelectors)) {
|
|
96
96
|
parentSelectors = [parentSelectors];
|
|
97
97
|
}
|
|
98
98
|
cy.log('clickButton ' + name);
|
|
99
|
-
return getDomNode([...parentSelectors, name])
|
|
99
|
+
return getDomNode([...parentSelectors, name], options)
|
|
100
100
|
.should('not.have.attr', 'data-disabled', 'true') // Check that the element is not disabled
|
|
101
101
|
.click({ force: true });
|
|
102
102
|
}
|
|
103
|
-
export function clickButtonIfEnabled(parentSelectors, name) { // allows button to be disabled
|
|
103
|
+
export function clickButtonIfEnabled(parentSelectors, name, options) { // allows button to be disabled
|
|
104
104
|
if (_.isString(parentSelectors)) {
|
|
105
105
|
parentSelectors = [parentSelectors];
|
|
106
106
|
}
|
|
107
|
-
return getDomNode([...parentSelectors, name])
|
|
107
|
+
return getDomNode([...parentSelectors, name], options)
|
|
108
108
|
.click({ force: true });
|
|
109
109
|
}
|
|
110
|
-
export function clickButtonIfExists(parentSelectors, name) {
|
|
110
|
+
export function clickButtonIfExists(parentSelectors, name, options) {
|
|
111
111
|
if (_.isString(parentSelectors)) {
|
|
112
112
|
parentSelectors = [parentSelectors];
|
|
113
113
|
}
|
|
114
|
-
return getDomNode([...parentSelectors, name]).if().then((node) => { // NOTE if() is a cypress-if function
|
|
114
|
+
return getDomNode([...parentSelectors, name], options).if().then((node) => { // NOTE if() is a cypress-if function
|
|
115
115
|
if (node) {
|
|
116
|
-
node.click();
|
|
116
|
+
cy.get(node).click();
|
|
117
117
|
}
|
|
118
118
|
});
|
|
119
119
|
}
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
fixInflector,
|
|
3
3
|
getLastPartOfPath,
|
|
4
4
|
bootstrapRouteWaiters,
|
|
5
|
+
stubWindowOpen,
|
|
5
6
|
} from './utilities.js';
|
|
6
7
|
import {
|
|
7
8
|
login,
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
import {
|
|
12
13
|
getDomNode,
|
|
13
14
|
getDomNodes,
|
|
15
|
+
ifExists,
|
|
14
16
|
} from './dom_functions.js';
|
|
15
17
|
import {
|
|
16
18
|
hasRowWithFieldValue,
|
|
@@ -116,6 +118,39 @@ export function crudJson(selector, newData, editData, schema, ancillaryData, lev
|
|
|
116
118
|
// do nothing for now
|
|
117
119
|
}
|
|
118
120
|
|
|
121
|
+
// Editor
|
|
122
|
+
export function viewPdf(editorSelector, formSelector) {
|
|
123
|
+
ifExists(formSelector, 'viewPdfBtn', (btn) => {
|
|
124
|
+
clickButton(formSelector, 'viewPdfBtn');
|
|
125
|
+
|
|
126
|
+
cy.wait(1000); // allow time for modal to render
|
|
127
|
+
const modalSelector = editorSelector + '/chooseFieldsForm';
|
|
128
|
+
clickButton(modalSelector, 'submitBtn');
|
|
129
|
+
cy.wait(1000); // allow time for pdf to download
|
|
130
|
+
cy.get('@windowOpen').should('be.calledWithMatch', /viewModelPdf/);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
export function emailPdf(editorSelector, formSelector) {
|
|
134
|
+
ifExists(formSelector, 'emailPdfBtn', (btn) => {
|
|
135
|
+
clickButton(formSelector, 'emailPdfBtn');
|
|
136
|
+
|
|
137
|
+
cy.wait(1000); // allow time for modal to render
|
|
138
|
+
const modalSelector = editorSelector + '/chooseFieldsForm';
|
|
139
|
+
clickButton(modalSelector, 'submitBtn');
|
|
140
|
+
cy.wait(1000); // allow time for new modal to render
|
|
141
|
+
|
|
142
|
+
// UI now shows the email modal
|
|
143
|
+
fillForm(modalSelector, { email: 'scott@onehat.com', message: 'Sample message', }, { email: 'Input', message: 'TextArea', });
|
|
144
|
+
clickButton(modalSelector, 'submitBtn');
|
|
145
|
+
cy.wait('@emailModelPdf');
|
|
146
|
+
|
|
147
|
+
getDomNode('InfoModal')
|
|
148
|
+
.should('exist')
|
|
149
|
+
.should('contain', 'Email sent successfully.');
|
|
150
|
+
clickButton('InfoModal', 'okBtn');
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
119
154
|
|
|
120
155
|
// Grid
|
|
121
156
|
export function crudWindowedGridRecord(gridSelector, newData, editData, schema, ancillaryData, level = 0) {
|
|
@@ -313,7 +348,11 @@ export function editGridRecord(gridSelector, fieldValues, schema, id, level = 0,
|
|
|
313
348
|
|
|
314
349
|
verifyNoErrorBox();
|
|
315
350
|
// cy.wait(1000);
|
|
316
|
-
|
|
351
|
+
|
|
352
|
+
if (whichEditor !== INLINE) {
|
|
353
|
+
viewPdf(editorSelector, formSelector);
|
|
354
|
+
emailPdf(editorSelector, formSelector);
|
|
355
|
+
}
|
|
317
356
|
}
|
|
318
357
|
export function editWindowedGridRecord(gridSelector, fieldValues, schema, id, level = 0) {
|
|
319
358
|
// edits the record as normal, then closes the editor window
|
|
@@ -542,6 +581,11 @@ export function editTreeRecord(treeSelector, fieldValues, schema, id, level = 0,
|
|
|
542
581
|
|
|
543
582
|
verifyNoErrorBox();
|
|
544
583
|
// cy.wait(1000);
|
|
584
|
+
|
|
585
|
+
if (whichEditor !== INLINE) {
|
|
586
|
+
viewPdf(editorSelector, formSelector);
|
|
587
|
+
emailPdf(editorSelector, formSelector);
|
|
588
|
+
}
|
|
545
589
|
|
|
546
590
|
}
|
|
547
591
|
export function editWindowedTreeRecord(treeSelector, fieldValues, schema, id, level = 0) {
|
|
@@ -591,6 +635,7 @@ export function runClosureTreeControlledManagerScreenCrudTests(model, schema, ne
|
|
|
591
635
|
navigateViaTabOrHomeButtonTo(url);
|
|
592
636
|
}
|
|
593
637
|
});
|
|
638
|
+
stubWindowOpen();
|
|
594
639
|
});
|
|
595
640
|
|
|
596
641
|
afterEach(function () {
|
|
@@ -688,6 +733,7 @@ export function runClosureTreeManagerScreenCrudTests(args) {
|
|
|
688
733
|
navigateViaTabOrHomeButtonTo(url);
|
|
689
734
|
}
|
|
690
735
|
});
|
|
736
|
+
stubWindowOpen();
|
|
691
737
|
});
|
|
692
738
|
|
|
693
739
|
// afterEach(function () {
|
|
@@ -753,6 +799,7 @@ export function runManagerScreenCrudTests(args) {
|
|
|
753
799
|
navigateViaTabOrHomeButtonTo(url);
|
|
754
800
|
}
|
|
755
801
|
});
|
|
802
|
+
stubWindowOpen();
|
|
756
803
|
});
|
|
757
804
|
|
|
758
805
|
// afterEach(function () {
|
|
@@ -806,11 +853,13 @@ export function runReportsManagerTests(reportData) {
|
|
|
806
853
|
beforeEach(function () {
|
|
807
854
|
bootstrapRouteWaiters();
|
|
808
855
|
login();
|
|
856
|
+
cy.restoreLocalStorage();
|
|
809
857
|
cy.url().then((currentUrl) => {
|
|
810
858
|
if (!currentUrl.endsWith(url)) {
|
|
811
859
|
navigateViaTabOrHomeButtonTo(url);
|
|
812
860
|
}
|
|
813
861
|
});
|
|
862
|
+
stubWindowOpen();
|
|
814
863
|
});
|
|
815
864
|
|
|
816
865
|
_.each(reportData, (report) => {
|
|
@@ -828,14 +877,14 @@ export function runReportsManagerTests(reportData) {
|
|
|
828
877
|
|
|
829
878
|
// Press Excel button
|
|
830
879
|
clickButton(selector, 'excelBtn');
|
|
831
|
-
cy.wait('@
|
|
880
|
+
cy.wait('@getReportWaiter', { timeout: 10000 }).then((interception) => {
|
|
832
881
|
expect(interception.response.headers['content-type']).to.include('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
833
882
|
});
|
|
834
883
|
|
|
835
884
|
|
|
836
885
|
// Press PDF button
|
|
837
886
|
clickButton(selector, 'pdfBtn');
|
|
838
|
-
cy.wait('@
|
|
887
|
+
cy.wait('@postReportWaiter', { timeout: 10000 }).then((interception) => {
|
|
839
888
|
expect(interception.response.headers['content-type']).to.include('pdf');
|
|
840
889
|
});
|
|
841
890
|
|
|
@@ -8,7 +8,14 @@
|
|
|
8
8
|
* @return Cypress chainer
|
|
9
9
|
*/
|
|
10
10
|
export function getDomNode(selectors, options = {}) {
|
|
11
|
-
|
|
11
|
+
let useFirst;
|
|
12
|
+
if (options?.hasOwnProperty('first')) {
|
|
13
|
+
useFirst = options.first;
|
|
14
|
+
delete options.first;
|
|
15
|
+
} else {
|
|
16
|
+
useFirst = true;
|
|
17
|
+
}
|
|
18
|
+
return cy.get(getTestIdSelectors(selectors, useFirst), options);
|
|
12
19
|
}
|
|
13
20
|
|
|
14
21
|
/**
|
|
@@ -22,6 +29,31 @@ export function getDomNodes(selectors, options = {}) {
|
|
|
22
29
|
return cy.get(getTestIdSelectors(selectors), options);
|
|
23
30
|
}
|
|
24
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Verifies that an element with the given selectors does not exist in the DOM.
|
|
34
|
+
* @argument {string | string[]} selectors - data-testid attribute values
|
|
35
|
+
* If an array is given, these will be considered nested selectors.
|
|
36
|
+
* e.g. ['parent', 'child'] will be converted to '[data-testid="parent"] [data-testid="child"]'
|
|
37
|
+
*/
|
|
38
|
+
export function verifyElementDoesNotExist(selectors) {
|
|
39
|
+
cy.get('body').then(($body) => {
|
|
40
|
+
const selectorString = getTestIdSelectors(selectors);
|
|
41
|
+
if ($body.find(selectorString).length > 0) {
|
|
42
|
+
throw new Error(`Element with selectors ${selectorString} exists in the DOM, when it should not`);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
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
|
+
|
|
25
57
|
/**
|
|
26
58
|
* Builds selector string for data-testid attributes.
|
|
27
59
|
* It leaves classname, id, and attribute selectors unchanged.
|
|
@@ -79,4 +111,23 @@ export function drag(draggableSelectors, droppableSelectors, options = {}) {
|
|
|
79
111
|
.then((success) => {
|
|
80
112
|
debugger;
|
|
81
113
|
});
|
|
82
|
-
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
// conditions functions that make use of cypress-if
|
|
118
|
+
export function ifExists(parentSelectors, name, cb) {
|
|
119
|
+
if (_.isString(parentSelectors)) {
|
|
120
|
+
parentSelectors = [parentSelectors];
|
|
121
|
+
}
|
|
122
|
+
return getDomNode([...parentSelectors, name]).if().then((node) => {
|
|
123
|
+
if (node) {
|
|
124
|
+
cb(node);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
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
|
+
}
|