@onehat/ui 0.4.25 → 0.4.27

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/ui",
3
- "version": "0.4.25",
3
+ "version": "0.4.27",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -7,14 +7,13 @@ export default function SquareButton(props) {
7
7
  const {
8
8
  text,
9
9
  isActive = false,
10
- activeColor,
10
+ activeClassName,
11
11
  invertColorWhenActive = false,
12
12
  showText = true,
13
13
  disableInteractions = false,
14
14
  fontSize = '20px',
15
15
  ...propsToPass
16
16
  } = props,
17
- bg = isActive ? activeColor || '#56a6f8' : '#fff',
18
17
  color = invertColorWhenActive && isActive ? '#fff' : '#000';
19
18
 
20
19
  if (!props.icon) {
@@ -24,21 +23,26 @@ export default function SquareButton(props) {
24
23
  throw Error('text missing. If you want to hide the text, use showText={false}');
25
24
  }
26
25
 
26
+ let className = `
27
+ SquareButton
28
+ rounded-md
29
+ p-2
30
+ h-[100px]
31
+ w-[100px]
32
+ flex
33
+ flex-col
34
+ justify-center
35
+ items-center
36
+ bg-grey-200
37
+ hover:bg-grey-400
38
+ disabled:bg-grey-100
39
+ `;
40
+ if (isActive && activeClassName) {
41
+ className += ' ' + activeClassName;
42
+ }
43
+
27
44
  return <IconButton
28
- className={`
29
- SquareButton
30
- rounded-md
31
- p-2
32
- bg-[${bg}]
33
- hover:bg-[${bg}]
34
- disabled:bg-[${bg}]
35
- h-[100px]
36
- w-[100px]
37
- flex
38
- flex-col
39
- justify-center
40
- items-center
41
- `}
45
+ className={className}
42
46
  style={{
43
47
  // backgroundColor: bg,
44
48
  }}
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  EDITOR_MODE__VIEW,
3
+ EDITOR_TYPE__SIDE,
3
4
  } from '../../Constants/Editor.js';
4
5
  import withComponent from '../Hoc/withComponent.js';
5
6
  import withPdfButtons from '../Hoc/withPdfButtons.js';
@@ -34,7 +35,10 @@ function Editor(props) {
34
35
  return null; // hide the editor when no selection
35
36
  }
36
37
 
37
- const propsToPass = _.omit(props, ['self', 'reference', 'parent']);
38
+ const propsToPass = _.omit(props, ['self', 'reference', 'parent', 'style']);
39
+ if (propsToPass.editorType === EDITOR_TYPE__SIDE) {
40
+ propsToPass.style = props.style; // side editor needs the style prop, but a windowed editor can get messed up if it's present (and withModal is used)!
41
+ }
38
42
 
39
43
  let canEdit = true;
40
44
  if (canRecordBeEdited && !canRecordBeEdited(selection)) {
@@ -77,7 +77,6 @@ function TagComponent(props) {
77
77
  showModal({
78
78
  body: <Editor
79
79
  editorType={EDITOR_TYPE__WINDOWED}
80
- {...propsToPass}
81
80
  parent={self}
82
81
  reference="viewer"
83
82
  isEditorViewOnly={true}
@@ -234,6 +233,7 @@ function TagComponent(props) {
234
233
  onView={() => onView(val)}
235
234
  onDelete={!isViewOnly ? () => onDelete(val) : null}
236
235
  showEye={showEye}
236
+ minimizeForRow={minimizeForRow}
237
237
  />;
238
238
  });
239
239
 
@@ -297,7 +297,7 @@ function TagComponent(props) {
297
297
  valueBoxesClassName += ' min-h-[25px] h-full overflow-auto flex-1';
298
298
  } else {
299
299
  // shrink both down
300
- valueBoxesClassName += ' h-auto min-h-[25px] max-h-[25px] overflow-auto flex-1';
300
+ valueBoxesClassName += ' Scott h-auto min-h-[25px] max-h-[35px] overflow-auto flex-1';
301
301
  comboClassName += ' h-auto min-h-0 max-h-[25px] flex-1';
302
302
  }
303
303
  }
@@ -15,6 +15,7 @@ export default function ValueBox(props) {
15
15
  onView,
16
16
  onDelete,
17
17
  showEye,
18
+ minimizeForRow = false,
18
19
  } = props,
19
20
  styles = UiGlobals.styles;
20
21
  return <HStackNative
@@ -31,20 +32,22 @@ export default function ValueBox(props) {
31
32
  ${!onDelete && 'pr-4'}
32
33
  `}
33
34
  >
34
- {showEye && <IconButton
35
- {...testProps('eyeBtn')}
36
- icon={Eye}
37
- _icon={{
38
- size: styles.FORM_TAG_VALUEBOX_ICON_SIZE,
39
- className: 'text-grey-600',
40
- }}
41
- onPress={onView}
42
- className={`
43
- ValueBox-eyeBtn
44
- h-full
45
- ${styles.FORM_TAG_BTN_CLASSNAME}
46
- `}
47
- />}
35
+ {showEye &&
36
+ <IconButton
37
+ {...testProps('eyeBtn')}
38
+ icon={Eye}
39
+ _icon={{
40
+ size: styles.FORM_TAG_VALUEBOX_ICON_SIZE,
41
+ className: 'text-grey-600',
42
+ }}
43
+ onPress={onView}
44
+ className={`
45
+ ValueBox-eyeBtn
46
+ h-full
47
+ ${minimizeForRow ? 'py-0' : ''}
48
+ ${styles.FORM_TAG_BTN_CLASSNAME}
49
+ `}
50
+ />}
48
51
  <Text
49
52
  className={`
50
53
  ValueBox-Text
@@ -52,6 +55,7 @@ export default function ValueBox(props) {
52
55
  ${styles.FORM_TAG_VALUEBOX_CLASSNAME}
53
56
  ${showEye ? 'ml-0' : 'ml-1'}
54
57
  ${onDelete ? 'mr-0' : 'mr-1'}
58
+ ${minimizeForRow ? 'py-0' : ''}
55
59
  `}
56
60
  >{text}</Text>
57
61
  {onDelete &&
@@ -66,6 +70,7 @@ export default function ValueBox(props) {
66
70
  className={`
67
71
  ValueBox-xBtn
68
72
  h-full
73
+ ${minimizeForRow ? 'py-0' : ''}
69
74
  ${styles.FORM_TAG_BTN_CLASSNAME}
70
75
  `}
71
76
  />}
@@ -153,6 +153,7 @@ function GridComponent(props) {
153
153
  alternateRowBackgrounds = true,
154
154
  alternatingInterval = 2,
155
155
  defaultRowHeight = 48,
156
+ getRowTestId,
156
157
 
157
158
  // The selectorSelected mechanism allows us to filter results of the primary model, (e.g. WorkOrders)
158
159
  // by the selection on the secondary model (e.g. Equipment). It's used on Grids, Trees, Forms, etc.
@@ -390,7 +391,7 @@ function GridComponent(props) {
390
391
 
391
392
  let rowComponent =
392
393
  <Pressable
393
- {...testProps((Repository ? Repository.schema.name : 'GridRow') + '-' + item?.id)}
394
+ {...testProps(getRowTestId ? getRowTestId(row) : ((Repository ? Repository.schema.name : 'GridRow') + '-' + item?.id))}
394
395
  onPress={(e) => {
395
396
  if (e.preventDefault && e.cancelable) {
396
397
  e.preventDefault();
@@ -416,7 +417,7 @@ function GridComponent(props) {
416
417
  if (canUser && !canUser(VIEW)) { // permissions
417
418
  return;
418
419
  }
419
- onView(true);
420
+ onView(!props.isEditorViewOnly);
420
421
  }
421
422
  } else {
422
423
  if (onEdit) {
@@ -597,6 +598,7 @@ function GridComponent(props) {
597
598
  `}
598
599
  >
599
600
  <ExpandButton
601
+ {...testProps((Repository ? Repository.schema.name : 'GridRow') + '-expandBtn-' + item?.id)}
600
602
  isExpanded={isExpanded}
601
603
  onToggle={() => setIsExpanded(index, !isExpanded)}
602
604
  _icon={{
@@ -1272,7 +1274,8 @@ function GridComponent(props) {
1272
1274
  </VStackNative>
1273
1275
 
1274
1276
  if (isDropTarget) {
1275
- grid = <Box
1277
+ grid = <VStackNative
1278
+ {...testProps(self, '-dropTarget')}
1276
1279
  ref={dropTargetRef}
1277
1280
  className={`
1278
1281
  Grid-dropTarget
@@ -1281,7 +1284,7 @@ function GridComponent(props) {
1281
1284
  border-[#0ff]
1282
1285
  ${canDrop && isOver ? "border-[4px]" : "border-[0px]"}
1283
1286
  `}
1284
- >{grid}</Box>
1287
+ >{grid}</VStackNative>
1285
1288
  }
1286
1289
  return grid;
1287
1290
  }
@@ -1,4 +1,4 @@
1
- import { useEffect, useState, useRef, } from 'react';
1
+ import { forwardRef, useEffect, useState, useRef, } from 'react';
2
2
  import {
3
3
  ADD,
4
4
  EDIT,
@@ -11,6 +11,7 @@ import {
11
11
  EDITOR_MODE__ADD,
12
12
  EDITOR_MODE__EDIT,
13
13
  EDITOR_TYPE__SIDE,
14
+ EDITOR_TYPE__INLINE,
14
15
  } from '../../../Constants/Editor.js';
15
16
  import Button from '../../Buttons/Button.js';
16
17
  import UiGlobals from '../../../UiGlobals.js';
@@ -20,10 +21,10 @@ import _ from 'lodash';
20
21
  // This HOC will eventually get out of sync with that one, and may need to be updated.
21
22
 
22
23
  export default function withSecondaryEditor(WrappedComponent, isTree = false) {
23
- return (props) => {
24
+ return forwardRef((props, ref) => {
24
25
 
25
26
  if (props.secondaryDisableWithEditor) {
26
- return <WrappedComponent {...props} isTree={isTree} />;
27
+ return <WrappedComponent {...props} ref={ref} isTree={isTree} />;
27
28
  }
28
29
 
29
30
  let [secondaryEditorMode, secondarySetEditorMode] = useState(EDITOR_MODE__VIEW); // Can change below, so use 'let'
@@ -52,6 +53,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
52
53
  secondaryNewEntityDisplayValue,
53
54
  secondaryNewEntityDisplayProperty, // in case the field to set for newEntityDisplayValue is different from model
54
55
  secondaryDefaultValues,
56
+ secondaryStayInEditModeOnSelectionChange = false,
55
57
 
56
58
  // withComponent
57
59
  self,
@@ -132,7 +134,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
132
134
  // NOTE: This is a hack to prevent adding a new record while the repository is still loading.
133
135
  // This can happen when the repository is still loading, and the user clicks the 'Add' button.
134
136
  setTimeout(() => {
135
- doAdd(e, values);
137
+ secondaryDoAdd(e, values);
136
138
  }, 500);
137
139
  return;
138
140
  }
@@ -169,7 +171,9 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
169
171
  if (!secondarySelection[0]) {
170
172
  throw Error('Must select a parent node.');
171
173
  }
172
- addValues.parentId = secondarySelection[0].id;
174
+ const parent = secondarySelection[0];
175
+ addValues.parentId = parent.id;
176
+ addValues.depth = parent.depth +1;
173
177
  } else {
174
178
  // Set repository to sort by id DESC and switch to page 1, so this new entity is guaranteed to show up on the current page, even after saving
175
179
  const currentSorter = SecondaryRepository.sorters[0];
@@ -200,10 +204,13 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
200
204
  setIsSaving(false);
201
205
  setSecondaryIsIgnoreNextSelectionChange(true);
202
206
  secondarySetSelection([entity]);
207
+ if (getListeners().onAfterAdd) {
208
+ await getListeners().onAfterAdd(entity);
209
+ }
203
210
  if (SecondaryRepository.isAutoSave) {
204
211
  // for isAutoSave Repositories, submit the handers right away
205
- if (getListeners().onAfterAdd) {
206
- await getListeners().onAfterAdd(entity);
212
+ if (getListeners().onAfterAddSave) {
213
+ await getListeners().onAfterAddSave(entity);
207
214
  }
208
215
  if (secondaryOnAdd) {
209
216
  await secondaryOnAdd(entity);
@@ -331,6 +338,13 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
331
338
  showPermissionsError(VIEW, secondaryModel);
332
339
  return;
333
340
  }
341
+ if (secondaryEditorType === EDITOR_TYPE__INLINE) {
342
+ alert('Cannot view in inline editor.');
343
+ return; // inline editor doesn't have a view mode
344
+ }
345
+
346
+ // check permissions for view
347
+
334
348
  if (secondarySelection.length !== 1) {
335
349
  return;
336
350
  }
@@ -350,6 +364,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
350
364
  showPermissionsError(DUPLICATE, secondaryModel);
351
365
  return;
352
366
  }
367
+
368
+ // check permissions for duplicate
369
+
370
+
353
371
  if (secondarySelection.length !== 1) {
354
372
  return;
355
373
  }
@@ -436,8 +454,8 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
436
454
  if (secondaryOnAdd) {
437
455
  await secondaryOnAdd(secondarySelection);
438
456
  }
439
- if (getListeners().onAfterAdd) {
440
- await getListeners().onAfterAdd(secondarySelection);
457
+ if (getListeners().onAfterAddSave) {
458
+ await getListeners().onAfterAddSave(secondarySelection);
441
459
  }
442
460
  setIsAdding(false);
443
461
  if (!canUser || canUser(EDIT, secondaryModel)) {
@@ -500,6 +518,16 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
500
518
  });
501
519
  },
502
520
  calculateEditorMode = (secondaryIsIgnoreNextSelectionChange = false) => {
521
+
522
+ let doStayInEditModeOnSelectionChange = secondaryStayInEditModeOnSelectionChange;
523
+ if (!_.isNil(UiGlobals.stayInEditModeOnSelectionChange)) {
524
+ // allow global override to for this property
525
+ doStayInEditModeOnSelectionChange = UiGlobals.stayInEditModeOnSelectionChange;
526
+ }
527
+ if (doStayInEditModeOnSelectionChange) {
528
+ secondaryIsIgnoreNextSelectionChange = true;
529
+ }
530
+
503
531
  // calculateEditorMode gets called only on selection changes
504
532
  let mode;
505
533
  if (secondaryEditorType === EDITOR_TYPE__SIDE && !_.isNil(UiGlobals.isSideEditorAlwaysEditMode) && UiGlobals.isSideEditorAlwaysEditMode) {
@@ -579,6 +607,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
579
607
 
580
608
  return <WrappedComponent
581
609
  {...props}
610
+ ref={ref}
582
611
  secondaryDisableWithEditor={false}
583
612
  secondaryCurrentRecord={secondaryCurrentRecord}
584
613
  secondarySetCurrentRecord={secondarySetCurrentRecord}
@@ -612,5 +641,5 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
612
641
  secondarySetSelection={secondarySetSelectionDecorated}
613
642
  isTree={isTree}
614
643
  />;
615
- };
644
+ });
616
645
  }
@@ -1,3 +1,4 @@
1
+ import { forwardRef } from 'react';
1
2
  import {
2
3
  EDITOR_TYPE__SIDE,
3
4
  } from '../../../Constants/Editor.js';
@@ -10,20 +11,21 @@ import _ from 'lodash';
10
11
 
11
12
 
12
13
  function withAdditionalProps(WrappedComponent) {
13
- return (props) => {
14
+ return forwardRef((props, ref) => {
14
15
  // provide the editorType to withEditor
15
16
  return <WrappedComponent
16
17
  editorType={EDITOR_TYPE__SIDE}
17
18
  {...props}
19
+ ref={ref}
18
20
  />;
19
- };
21
+ });
20
22
  }
21
23
 
22
24
  // NOTE: Effectivtly, the HOC composition is:
23
25
  // withAdditionalProps(withSecondaryEditor(withSecondarySideEditor))
24
26
 
25
27
  export default function withSecondarySideEditor(WrappedComponent, isTree = false) {
26
- return withAdditionalProps(withSecondaryEditor((props) => {
28
+ const SideEditor = forwardRef((props, ref) => {
27
29
  const {
28
30
  SecondaryEditor,
29
31
  secondaryEditorProps = {},
@@ -36,6 +38,7 @@ export default function withSecondarySideEditor(WrappedComponent, isTree = false
36
38
  secondarySelectorId,
37
39
  secondarySelectorSelected,
38
40
  secondarySelectorSelectedField,
41
+ style,
39
42
 
40
43
  ...propsToPass
41
44
  } = props;
@@ -44,8 +47,23 @@ export default function withSecondarySideEditor(WrappedComponent, isTree = false
44
47
  throw Error('SecondaryEditor is not defined');
45
48
  }
46
49
 
50
+ if (isResizable) {
51
+ secondaryEditorProps.w = 500;
52
+ secondaryEditorProps.isResizable = true;
53
+ } else {
54
+ secondaryEditorProps.flex = secondarySideFlex;
55
+ }
56
+
57
+ if (!secondaryEditorProps.className) {
58
+ secondaryEditorProps.className = '';
59
+ }
60
+ secondaryEditorProps.className += ' border-l-1 border-l-grey-300';
61
+
47
62
  return <Container
63
+ parent={self}
64
+ reference="SideEditor"
48
65
  center={<WrappedComponent
66
+ ref={ref}
49
67
  isTree={isTree}
50
68
  isSideEditor={true}
51
69
  {...props}
@@ -53,13 +71,11 @@ export default function withSecondarySideEditor(WrappedComponent, isTree = false
53
71
  east={<Editor
54
72
  {...propsToPass}
55
73
  editorType={EDITOR_TYPE__SIDE}
56
- flex={secondarySideFlex}
57
- borderLeftWidth={1}
58
- borderLeftColor="#ccc"
59
74
  {...secondaryEditorProps}
60
75
  parent={self}
61
76
  reference="secondaryEditor"
62
77
  />}
63
78
  />;
64
- }));
79
+ });
80
+ return withAdditionalProps(withSecondaryEditor(SideEditor, isTree));
65
81
  }
@@ -1,3 +1,4 @@
1
+ import { forwardRef } from 'react';
1
2
  import {
2
3
  Modal, ModalBackdrop, ModalHeader, ModalContent, ModalCloseButton, ModalBody, ModalFooter,
3
4
  } from '@project-components/Gluestack';
@@ -13,20 +14,21 @@ import _ from 'lodash';
13
14
 
14
15
 
15
16
  function withAdditionalProps(WrappedComponent) {
16
- return (props) => {
17
+ return forwardRef((props, ref) => {
17
18
  // provide the editorType to withEditor
18
19
  return <WrappedComponent
19
20
  editorType={EDITOR_TYPE__WINDOWED}
20
21
  {...props}
22
+ ref={ref}
21
23
  />;
22
- };
24
+ });
23
25
  }
24
26
 
25
27
  // NOTE: Effectivtly, the HOC composition is:
26
28
  // withAdditionalProps(withSecondaryEditor(withSecondaryWindowedEditor))
27
29
 
28
30
  export default function withSecondaryWindowedEditor(WrappedComponent, isTree = false) {
29
- return withAdditionalProps(withSecondaryEditor((props) => {
31
+ const WindowedEditor = forwardRef((props, ref) => {
30
32
  const {
31
33
  secondaryIsEditorShown = false,
32
34
  secondarySetIsEditorShown,
@@ -41,6 +43,7 @@ export default function withSecondaryWindowedEditor(WrappedComponent, isTree = f
41
43
  secondarySelectorSelected,
42
44
  secondarySelectorSelectedField,
43
45
  h,
46
+ style,
44
47
 
45
48
  ...propsToPass
46
49
  } = props;
@@ -65,25 +68,28 @@ export default function withSecondaryWindowedEditor(WrappedComponent, isTree = f
65
68
  }
66
69
 
67
70
  return <>
68
- <WrappedComponent {...props} />
71
+ <WrappedComponent {...props} ref={ref} />
69
72
  {secondaryIsEditorShown &&
70
73
  <Modal
71
74
  isOpen={true}
72
75
  onClose={() => secondarySetIsEditorShown(false)}
76
+ className="withSecondaryEditor-Modal"
73
77
  >
74
- <ModalBackdrop />
75
- <ModalContent>
76
- <ModalBody>
77
- <SecondaryEditor
78
- editorType={EDITOR_TYPE__WINDOWED}
79
- {...propsToPass}
80
- {...secondaryEditorProps}
81
- parent={self}
82
- reference="secondaryEditor"
83
- />
84
- </ModalBody>
85
- </ModalContent>
78
+ <ModalBackdrop className="withSecondaryEditor-ModalBackdrop" />
79
+ <SecondaryEditor
80
+ editorType={EDITOR_TYPE__WINDOWED}
81
+ {...propsToPass}
82
+ {...secondaryEditorProps}
83
+ parent={self}
84
+ reference="secondaryEditor"
85
+ className={`
86
+ bg-white
87
+ shadow-lg
88
+ rounded-lg
89
+ `}
90
+ />
86
91
  </Modal>}
87
92
  </>;
88
- }, isTree));
93
+ });
94
+ return withAdditionalProps(withSecondaryEditor(WindowedEditor, isTree));
89
95
  }
@@ -163,13 +163,14 @@ export default function withModal(WrappedComponent) {
163
163
  <Panel
164
164
  title={title}
165
165
  isCollapsible={false}
166
- className="bg-white overflow-auto"
166
+ className="withModal-Panel bg-white"
167
167
  h={h > windowHeight ? windowHeight : h}
168
168
  w={w > windowWidth ? windowWidth : w}
169
169
  isWindow={true}
170
170
  disableAutoFlex={true}
171
171
  onClose={canClose ? hideModal : null}
172
172
  footer={footer}
173
+ isScrollable={true}
173
174
  >{modalBody}</Panel>
174
175
  }
175
176
  }
@@ -83,7 +83,13 @@ export default function withPdfButtons(WrappedComponent) {
83
83
 
84
84
  if (!_.isEmpty(ancillaryItems)) {
85
85
  const
86
- ancillaryItemsClone = _.cloneDeep(ancillaryItems),
86
+ ancillaryItemsClone = _.cloneDeepWith(ancillaryItems, (value) => {
87
+ // Exclude the 'parent' property from being cloned, as it would introduce an infinitely recursive loop
88
+ if (value && value.parent) {
89
+ const { parent, ...rest } = value;
90
+ return rest;
91
+ }
92
+ }),
87
93
  items = [];
88
94
  _.each(ancillaryItemsClone, (ancillaryItem) => { // clone, as we don't want to alter the item by reference
89
95
  let name;
@@ -3,7 +3,7 @@ import { Path, Svg } from 'react-native-svg';
3
3
 
4
4
  const SvgComponent = createIcon({
5
5
  Root: Svg,
6
- viewBox: '0 0 512 512',
6
+ viewBox: '0 0 101.44 83.8',
7
7
  path: <Path d="M58.92 4.5L99.9 68.86c4.12 6.47-.53 14.94-8.2 14.94H9.74c-7.67 0-12.32-8.47-8.2-14.94L42.52 4.5c3.82-6 12.58-6 16.4 0zm-8.2 68.21c3.24 0 5.34-2.34 5.34-5.46-.06-3.18-2.16-5.46-5.34-5.46s-5.4 2.28-5.4 5.46 2.16 5.46 5.4 5.46zm3.42-13.92l1.32-27.18h-9.54l1.38 27.18h6.84z" strokeWidth={0} />
8
8
  });
9
9
 
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  HStack,
3
+ Icon,
3
4
  Text,
4
5
  VStackNative,
5
6
  } from '@project-components/Gluestack';
@@ -98,7 +99,16 @@ function ManagerScreen(props) {
98
99
  className="max-h-screen overflow-hidden flex-1 w-full"
99
100
  >
100
101
  <HStack className="h-[80px] items-center border-b-[2px] border-b-[#ccc]">
101
- <Text {...textProps} className="pl-5 text-[26px] font-[700]">{title}</Text>
102
+ {props.icon ?
103
+ <Icon
104
+ as={props.icon}
105
+ className={`
106
+ ml-5
107
+ `}
108
+ size="xl"
109
+ color="#000"
110
+ /> : null}
111
+ <Text {...textProps} className="pl-4 text-[26px] font-[700]">{title}</Text>
102
112
  {allowSideBySide &&
103
113
  <>
104
114
  <IconButton
@@ -47,8 +47,9 @@ export default function PaginationToolbar(props) {
47
47
  minimize={minimize}
48
48
  disablePageSize={disablePageSize}
49
49
  />
50
- {toolbarItems.length &&
50
+ {toolbarItems.length ?
51
51
  <HStack className={`
52
+ PaginationToolbar-HStack
52
53
  flex-1
53
54
  space-x-1
54
55
  border-l
@@ -56,6 +57,6 @@ export default function PaginationToolbar(props) {
56
57
  ml-3
57
58
  pl-3
58
59
  `}
59
- >{toolbarItems}</HStack>}
60
+ >{toolbarItems}</HStack> : null}
60
61
  </Toolbar>;
61
62
  };
@@ -24,9 +24,11 @@ export function getDomNodes(selectors, options = {}) {
24
24
 
25
25
  /**
26
26
  * Builds selector string for data-testid attributes.
27
- * @argument {string | string[]} selectors - data-testid attribute values
28
- * If an array is given, these will be considered nested selectors.
27
+ * It leaves classname, id, and attribute selectors unchanged.
28
+ *
29
+ * If selectors is an array, these will be considered nested selectors.
29
30
  * e.g. ['parent', 'child'] will be converted to '[data-testid="parent"] [data-testid="child"]'
31
+ * @argument {string|string[]} selectors - data-testid attribute values
30
32
  * @return {string}
31
33
  */
32
34
  export function getTestIdSelectors(selectors, isGetFirst = false) {
@@ -34,10 +36,47 @@ export function getTestIdSelectors(selectors, isGetFirst = false) {
34
36
  selectors = [selectors];
35
37
  }
36
38
  const selectorParts = _.map(selectors, (selector) => {
37
- if (selector.match(/=/)) { // selector is something like [role="switch"], so don't use data-testid
38
- return selector;
39
+ if (!selector.match(/^\./) // className, like .my-class
40
+ && !selector.match(/^#/) // id, like @my-id
41
+ && !selector.match(/=/) // attribute, like [role="switch"]
42
+ ){
43
+ selector = '[data-testid="' + selector + '"]';
44
+ }
45
+ if (isGetFirst) {
46
+ selector += ':first';
39
47
  }
40
- return '[data-testid="' + selector + '"]' + (isGetFirst ? ':first' : '');
48
+ return selector;
41
49
  });
42
50
  return selectorParts.join(' ');
43
51
  }
52
+
53
+ export function drag(draggableSelectors, droppableSelectors, options = {}) {
54
+ if (typeof options.force === 'undefined') {
55
+ options.force = true;
56
+ }
57
+
58
+ // getDomNode(getTestIdSelectors(droppableSelectors)).then((node) => {
59
+ // const selectors = getTestIdSelectors(droppableSelectors);
60
+ // debugger;
61
+ // });
62
+
63
+ options = {
64
+ source: { // applies to the element being dragged
65
+ x: 10,
66
+ // y: 100,
67
+ position: 'left',
68
+ },
69
+ target: { // applies to the drop target
70
+ position: 'left',
71
+ x: 20,
72
+ },
73
+ force: true, // applied to both the source and target element
74
+ };
75
+
76
+
77
+ return getDomNode(draggableSelectors)
78
+ .drag(getTestIdSelectors(droppableSelectors), options)
79
+ .then((success) => {
80
+ debugger;
81
+ });
82
+ }
@@ -4,7 +4,7 @@ import UiGlobals from '../UiGlobals.js';
4
4
  // This adds a data-testid attribute to the DOM node,
5
5
  // which can be quried in Cypress by: document.querySelector(`[data-testid='MyTestId']`);
6
6
 
7
- export default function testProps(id) {
7
+ export default function testProps(id, suffix) {
8
8
  if (!UiGlobals.debugMode) {
9
9
  return {};
10
10
  }
@@ -19,6 +19,9 @@ export default function testProps(id) {
19
19
  if (id.match(/\s/g)) {
20
20
  id = id.replace(/\s/g, '_'); // convert any spaces to underscores
21
21
  }
22
+ if (suffix) {
23
+ id += suffix; // this is used in conjunction with 'self' object
24
+ }
22
25
  if (!window && Platform.OS === 'android') {
23
26
  return {
24
27
  accessibilityLabel: id,
@@ -1,14 +0,0 @@
1
- import Container from '../Container/Container.js';
2
- import emptyFn from '../../Functions/emptyFn.js';
3
- import _ from 'lodash';
4
-
5
- export default function EditorWindow(props) {
6
- const {
7
- title,
8
- isOpen = false,
9
- onClose = emptyFn,
10
- ...propsToPass
11
- } = props;
12
-
13
-
14
- }