@onehat/ui 0.4.34 → 0.4.37

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.
@@ -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 = mode;
133
+ if (editorModeRef.current !== mode) {
134
+ editorModeRef.current = mode;
135
+ forceUpdate();
136
+ }
132
137
  },
133
138
  getNewEntityDisplayValue = () => {
134
139
  return newEntityDisplayValueRef.current;
@@ -33,6 +33,7 @@ export default function withModal(WrappedComponent) {
33
33
  [okBtnLabel, setOkBtnLabel] = useState(),
34
34
  [onYes, setOnYes] = useState(),
35
35
  [onNo, setOnNo] = useState(),
36
+ [onCancel, setOnCancel] = useState(),
36
37
  [customButtons, setCustomButtons] = useState(),
37
38
  [body, setBody] = useState(),
38
39
  [whichModal, setWhichModal] = useState(),
@@ -40,9 +41,6 @@ export default function withModal(WrappedComponent) {
40
41
  autoFocusRef = useRef(null),
41
42
  cancelRef = useRef(null),
42
43
  [windowWidth, windowHeight] = useAdjustedWindowSize(w, h),
43
- onCancel = () => {
44
- hideModal();
45
- },
46
44
  hideModal = () => {
47
45
  setIsModalShown(false);
48
46
  },
@@ -52,6 +50,7 @@ export default function withModal(WrappedComponent) {
52
50
  body = null,
53
51
  canClose = false,
54
52
  includeCancel = false,
53
+ onCancel = null,
55
54
  onOk = null,
56
55
  okBtnLabel = null,
57
56
  onYes = null,
@@ -76,6 +75,7 @@ export default function withModal(WrappedComponent) {
76
75
  setBody(body);
77
76
  setCanClose(canClose);
78
77
  setIncludeCancel(includeCancel);
78
+ setOnCancel(() => onCancel);
79
79
  setOnOk(onOk ? () => onOk : null);
80
80
  setOkBtnLabel(okBtnLabel || 'OK');
81
81
  setOnYes(onYes ? () => onYes : null);
@@ -101,7 +101,7 @@ export default function withModal(WrappedComponent) {
101
101
  buttons.push(<Button
102
102
  {...testProps('cancelBtn')}
103
103
  key="cancelBtn"
104
- onPress={onCancel}
104
+ onPress={onCancel || hideModal}
105
105
  colorScheme="coolGray"
106
106
  ref={cancelRef}
107
107
  className="mr-2"
@@ -180,7 +180,7 @@ export default function withModal(WrappedComponent) {
180
180
  {...props}
181
181
  disableWithModal={false}
182
182
  showModal={showModal}
183
- hideModal={onCancel}
183
+ hideModal={onCancel || hideModal}
184
184
  updateModalBody={updateModalBody}
185
185
  isModalShown={isModalShown}
186
186
  whichModal={whichModal}
@@ -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
- setSelection([]);
122
+ if (!_.isEmpty(getSelection())) {
123
+ setSelection([]);
124
+ }
124
125
  },
125
126
  refreshSelection = () => {
126
127
  // When Repository reloads, the entities get destroyed.
@@ -33,12 +33,9 @@ function Report(props) {
33
33
  title,
34
34
  description,
35
35
  reportId,
36
- // icon,
37
36
  disablePdf = false,
38
37
  disableExcel = false,
39
- includePresets = false,
40
38
  showReportHeaders = true,
41
- h = '300px',
42
39
  } = props,
43
40
  buttons = [];
44
41
 
@@ -68,6 +65,7 @@ function Report(props) {
68
65
  reportType: REPORT_TYPES__EXCEL,
69
66
  showReportHeaders,
70
67
  }),
68
+ disabledOnInvalid: true,
71
69
  });
72
70
  }
73
71
  if (!disablePdf) {
@@ -82,6 +80,7 @@ function Report(props) {
82
80
  reportType: REPORT_TYPES__PDF,
83
81
  showReportHeaders,
84
82
  }),
83
+ disableOnInvalid: true,
85
84
  });
86
85
  }
87
86
  return <VStackNative
@@ -234,7 +234,12 @@ function Viewer(props) {
234
234
  }
235
235
  }
236
236
 
237
- let elementClassName = 'Viewer-field-' + name;
237
+ let elementClassName = `
238
+ Viewer-field
239
+ basis-auto
240
+ grow
241
+ shrink
242
+ `;
238
243
  const defaultsClassName = defaults.className;
239
244
  if (defaultsClassName) {
240
245
  elementClassName += ' ' + defaultsClassName;
@@ -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) => { // NOTE if() is a cypress-if function
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
- // * Given a form,
280
- // * return a url-encoded string representing all keys and values
281
- // *
282
- // * @param {jQuery} form
283
- // * @return {String}
284
- // */
285
- // export function formSerialize(form) {
286
- // 'use strict';
287
- // var i, j, len, jLen, formElement,
288
- // q = [],
289
- // theForm = form[0],
290
- // varCounters = {};
291
-
292
- // function addNameValue(name, value) { // create this function so I can use varCounters for
293
- // var matches = name.match(/([\w\d]+)\[\]/i),
294
- // varName,
295
- // ix = 0;
296
- // if (matches && matches[1]) {
297
- // varName = matches[1];
298
- // if (typeof varCounters[varName] === 'undefined') {
299
- // varCounters[varName] = ix;
300
- // } else {
301
- // ix = ++varCounters[varName];
302
- // }
303
- // name = varName + '[' + ix + ']';
304
- // }
305
- // q.push(urlencode(name) + '=' + urlencode(value));
306
- // }
307
-
308
- // if (!theForm || !theForm.nodeName || theForm.nodeName.toLowerCase() !== 'form') {
309
- // throw 'You must supply a form element';
310
- // }
311
- // for (i = 0, len = theForm.elements.length; i < len; i++) {
312
- // formElement = theForm.elements[i];
313
- // if (formElement.name === '' || formElement.disabled) {
314
- // continue;
315
- // }
316
- // switch (formElement.nodeName.toLowerCase()) {
317
- // case 'input':
318
- // switch (formElement.type) {
319
- // case 'text':
320
- // case 'hidden':
321
- // case 'password':
322
- // case 'button': // Not submitted when submitting form manually, though jQuery does serialize this and it can be an HTML4 successful control
323
- // case 'submit':
324
- // addNameValue(formElement.name, formElement.value);
325
- // break;
326
- // case 'checkbox':
327
- // case 'radio':
328
- // if (formElement.checked) {
329
- // addNameValue(formElement.name, formElement.value);
330
- // } else if (formElement.value === '1') {
331
- // addNameValue(formElement.name, '0'); // Submit actual value of zero for booleans, instead of no value at all
332
- // }
333
- // break;
334
- // case 'file':
335
- // // addNameValue(formElement.name, formElement.value); // Will work and part of HTML4 "successful controls", but not used in jQuery
336
- // break;
337
- // case 'reset':
338
- // break;
339
- // }
340
- // break;
341
- // case 'textarea':
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
- // * 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
- // }
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
- // * Validate that form values match what they're supposed to
431
- // */
432
- // export function validateFormValues(data, schema) {
433
- // cy.wrap().then(() => { // Wrap this in a Cypress promise, so it executes in correct order, relative to other Cypress promises
434
-
435
- // const formValues = getFormValues(schema);
436
- // let diff = deepDiffObj(formValues, data);
437
-
438
- // // SPECIAL CASE: Omit password fields from diff
439
- // const omitFields = [];
440
- // _.each(diff, (value, key) => {
441
- // if (key.match(/^password/i)) {
442
- // omitFields.push(key);
443
- // }
444
- // });
445
- // if (omitFields.length) {
446
- // diff = _.omit(diff, omitFields);
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
- // // If there are still any differences, log them
450
- // if (_.keys(diff).length > 0) {
451
- // console.log('data', data);
452
- // console.log('formValues', formValues);
453
- // console.log('diff', diff);
454
- // }
455
-
456
- // expect(diff).to.deep.equal({});
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
- // export function getComboValue(fieldName) {
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
 
@@ -21,7 +21,6 @@ export default function buildAdditionalButtons(configs, self, handlerArgs = {})
21
21
  text,
22
22
  icon,
23
23
  isDisabled,
24
- className: 'ml-2',
25
24
  tooltip,
26
25
  color,
27
26
  };