@controleonline/ui-default 1.0.273 → 1.0.274

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": "@controleonline/ui-default",
3
- "version": "1.0.273",
3
+ "version": "1.0.274",
4
4
  "author": "ControleOnline",
5
5
  "description": "ControleOnline Default Quasar UI Component",
6
6
  "license": "MIT",
@@ -1,26 +1,21 @@
1
1
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
- import { Modal, Text, TouchableOpacity, View, useWindowDimensions } from 'react-native';
2
+ import { useWindowDimensions } from 'react-native';
3
3
  import { NavigationRouteContext, useIsFocused } from '@react-navigation/native';
4
4
  import { useStore } from '@store';
5
5
  import { useMessage } from '@controleonline/ui-common/src/react/components/MessageService';
6
- import Icon from 'react-native-vector-icons/Feather';
7
6
  import { normalizeText } from '../inputs/defaultInputUtils';
8
7
  import { DEFAULT_COMPACT_BREAKPOINT, isObject, stableSerialize } from './DefaultTable.utils';
9
8
  import {
10
9
  resolveDefaultTablePreferenceScope,
11
10
  resolveStoredTableViewModePreference,
12
11
  } from '../../utils/tableVisibleColumnsPreferences';
13
- import DefaultTableBody from './DefaultTableBody';
14
- import DefaultTableFooter from './DefaultTableFooter';
15
- import DefaultTableToolbar from './DefaultTableToolbar';
16
- import DefaultForm from '../form/DefaultForm';
17
- import styles from './DefaultTable.styles';
18
12
  import { useDefaultTablePagination } from './useDefaultTablePagination';
19
13
  import {
20
14
  useDefaultTableSortedData,
21
15
  useDefaultTableSortState,
22
16
  } from './useDefaultTableSorting';
23
17
  import useDefaultTableTheme from './useDefaultTableTheme';
18
+ import DefaultTableView from './DefaultTableView';
24
19
 
25
20
  import {
26
21
  assignGetterValue,
@@ -80,6 +75,8 @@ const DefaultTable = ({
80
75
  showSearch = null,
81
76
  showRowActions = true,
82
77
  showToolbar = true,
78
+ showToolbarActions = null,
79
+ showToolbarControls = null,
83
80
  showTotalItemsInCompactToolbar = false,
84
81
  showTotalItemsInFooter = true,
85
82
  sort = null,
@@ -292,6 +289,14 @@ const DefaultTable = ({
292
289
  showSearch,
293
290
  showRowActions: storeDeclaredConfigs.showRowActions === false ? false : showRowActions,
294
291
  showToolbar,
292
+ showToolbarActions:
293
+ showToolbarActions === null
294
+ ? storeDeclaredConfigs.showToolbarActions !== false
295
+ : showToolbarActions !== false,
296
+ showToolbarControls:
297
+ showToolbarControls === null
298
+ ? storeDeclaredConfigs.showToolbarControls !== false
299
+ : showToolbarControls !== false,
295
300
  showTotalItemsInCompactToolbar,
296
301
  showTotalItemsInFooter,
297
302
  sortedData,
@@ -352,6 +357,8 @@ const DefaultTable = ({
352
357
  showSearch,
353
358
  showRowActions,
354
359
  showToolbar,
360
+ showToolbarActions,
361
+ showToolbarControls,
355
362
  showTotalItemsInCompactToolbar,
356
363
  showTotalItemsInFooter,
357
364
  sortedData,
@@ -391,6 +398,8 @@ const DefaultTable = ({
391
398
  showSearch,
392
399
  showRowActions,
393
400
  showToolbar,
401
+ showToolbarActions,
402
+ showToolbarControls,
394
403
  showTotalItemsInCompactToolbar,
395
404
  showTotalItemsInFooter,
396
405
  storedViewMode,
@@ -427,6 +436,8 @@ const DefaultTable = ({
427
436
  showSearch,
428
437
  showRowActions,
429
438
  showToolbar,
439
+ showToolbarActions,
440
+ showToolbarControls,
430
441
  showTotalItemsInCompactToolbar,
431
442
  showTotalItemsInFooter,
432
443
  storedViewMode,
@@ -457,87 +468,27 @@ const DefaultTable = ({
457
468
  onDataLoaded?.(sortedData);
458
469
  }, [onDataLoaded, sortedData]);
459
470
 
460
- return (
461
- <View
462
- style={[
463
- styles.wrap,
464
- {
465
- borderWidth: tablePanelBorderColor ? 1 : 0,
466
- borderColor: tablePanelBorderColor,
467
- backgroundColor: themeColors.panelBackground,
468
- },
469
- ]}
470
- >
471
- {showToolbar !== false ? <DefaultTableToolbar storeName={storeName} /> : null}
472
- <DefaultTableBody storeName={storeName} />
473
- <DefaultTableFooter storeName={storeName} />
474
- {shouldRenderBottomAddButton ? (
475
- <View style={styles.bottomAddBar}>
476
- <TouchableOpacity
477
- accessibilityRole="button"
478
- accessibilityLabel={resolvedAddLabel}
479
- activeOpacity={0.84}
480
- style={[
481
- styles.bottomAddButton,
482
- { backgroundColor: floatingAddBackgroundColor },
483
- ]}
484
- onPress={resolvedOnAdd}
485
- >
486
- <Icon name="plus" size={18} color={floatingAddIconColor} />
487
- <Text style={[styles.bottomAddText, { color: floatingAddIconColor }]}>
488
- {resolvedAddLabel}
489
- </Text>
490
- </TouchableOpacity>
491
- </View>
492
- ) : null}
493
- {shouldRenderFloatingAddButton ? (
494
- <TouchableOpacity
495
- accessibilityRole="button"
496
- accessibilityLabel={resolvedAddLabel}
497
- activeOpacity={0.84}
498
- style={[
499
- styles.floatingAddButton,
500
- { backgroundColor: floatingAddBackgroundColor },
501
- ]}
502
- onPress={resolvedOnAdd}
503
- >
504
- <Icon name="plus" size={24} color={floatingAddIconColor} />
505
- </TouchableOpacity>
506
- ) : null}
507
- <Modal
508
- animationType="fade"
509
- onRequestClose={closeCreateForm}
510
- transparent
511
- visible={isCreateFormOpen}
512
- >
513
- <View style={styles.modalOverlay}>
514
- <View style={styles.modalCard}>
515
- <View style={styles.modalHeader}>
516
- <Text style={styles.modalTitle}>{resolvedAddLabel}</Text>
517
- <TouchableOpacity
518
- accessibilityRole="button"
519
- accessibilityLabel={global.t?.t(storeName, 'button', 'cancel') || 'Cancelar'}
520
- onPress={closeCreateForm}
521
- style={styles.modalCloseButton}
522
- >
523
- <Icon name="x" size={16} color={themeColors.textPrimary || '#0F172A'} />
524
- </TouchableOpacity>
525
- </View>
526
- <DefaultForm
527
- actions={resolvedActions}
528
- columns={columnsForTable}
529
- getOptionsForColumn={getOptionsForColumn}
530
- mode="create"
531
- onCancel={closeCreateForm}
532
- onSaved={handleDefaultCreateSaved}
533
- row={requestParamsSeed}
534
- storeName={storeName}
535
- />
536
- </View>
537
- </View>
538
- </Modal>
539
- </View>
540
- );
471
+ return <DefaultTableView {...{
472
+ tablePanelBorderColor,
473
+ themeColors,
474
+ showToolbar,
475
+ storeName,
476
+ showToolbarActions,
477
+ showToolbarControls,
478
+ shouldRenderBottomAddButton,
479
+ resolvedAddLabel,
480
+ floatingAddBackgroundColor,
481
+ resolvedOnAdd,
482
+ floatingAddIconColor,
483
+ shouldRenderFloatingAddButton,
484
+ closeCreateForm,
485
+ isCreateFormOpen,
486
+ resolvedActions,
487
+ columnsForTable,
488
+ getOptionsForColumn,
489
+ handleDefaultCreateSaved,
490
+ requestParamsSeed
491
+ }} />;
541
492
  };
542
493
 
543
494
  export default DefaultTable;
@@ -34,7 +34,11 @@ const resolveConfiguredLabel = config => {
34
34
  return global.t?.t?.('defaultTable', 'button', 'import');
35
35
  };
36
36
 
37
- const DefaultTableToolbar = ({ storeName }) => {
37
+ const DefaultTableToolbar = ({
38
+ storeName,
39
+ showToolbarActions: showToolbarActionsProp,
40
+ showToolbarControls: showToolbarControlsProp,
41
+ }) => {
38
42
  const store = useStore(storeName);
39
43
  const [isImportModalVisible, setIsImportModalVisible] = useState(false);
40
44
  const { width } = useWindowDimensions();
@@ -44,6 +48,14 @@ const DefaultTableToolbar = ({ storeName }) => {
44
48
  const showTotalItemsInCompactToolbar = configs.showTotalItemsInCompactToolbar === true;
45
49
  const showTotalItemsInFooter = configs.showTotalItemsInFooter !== false;
46
50
  const toolbarActions = Array.isArray(configs.toolbarActions) ? configs.toolbarActions : [];
51
+ const showToolbarActions =
52
+ showToolbarActionsProp === null || showToolbarActionsProp === undefined
53
+ ? configs.showToolbarActions !== false
54
+ : showToolbarActionsProp !== false;
55
+ const showToolbarControls =
56
+ showToolbarControlsProp === null || showToolbarControlsProp === undefined
57
+ ? configs.showToolbarControls !== false
58
+ : showToolbarControlsProp !== false;
47
59
  const buildTransferAction = (action, fallback) => {
48
60
  if (!action) return null;
49
61
 
@@ -99,11 +111,9 @@ const DefaultTableToolbar = ({ storeName }) => {
99
111
  label: global.t?.t?.('defaultTable', 'button', 'export'),
100
112
  },
101
113
  );
102
- const resolvedToolbarActions = [
103
- importTableAction,
104
- exportTableAction,
105
- ...toolbarActions,
106
- ].filter(Boolean);
114
+ const resolvedToolbarActions = showToolbarActions
115
+ ? [importTableAction, exportTableAction, ...toolbarActions].filter(Boolean)
116
+ : [];
107
117
  const { tableBorderColors, toolbarColors } = useDefaultTableTheme();
108
118
  const {
109
119
  backgroundColor: toolbarBackgroundColor,
@@ -178,7 +188,7 @@ const DefaultTableToolbar = ({ storeName }) => {
178
188
  <DefaultTableCollapsedSearch storeName={storeName} />
179
189
  <View style={styles.toolbarActionGroup}>
180
190
  {!shouldRenderCompactToolbarTotalItems ? renderToolbarActions() : null}
181
- <DefaultTableControls storeName={storeName} />
191
+ {showToolbarControls ? <DefaultTableControls storeName={storeName} /> : null}
182
192
  </View>
183
193
  </View>
184
194
  <DefaultTableImportModal
@@ -0,0 +1,118 @@
1
+ import React from 'react';
2
+ import { Modal, Text, TouchableOpacity, View } from 'react-native';
3
+ import Icon from 'react-native-vector-icons/Feather';
4
+ import DefaultTableBody from './DefaultTableBody';
5
+ import DefaultTableFooter from './DefaultTableFooter';
6
+ import DefaultTableToolbar from './DefaultTableToolbar';
7
+ import DefaultForm from '../form/DefaultForm';
8
+ import styles from './DefaultTable.styles';
9
+
10
+ export default function DefaultTableView({
11
+ tablePanelBorderColor,
12
+ themeColors,
13
+ showToolbar,
14
+ storeName,
15
+ showToolbarActions,
16
+ showToolbarControls,
17
+ shouldRenderBottomAddButton,
18
+ resolvedAddLabel,
19
+ floatingAddBackgroundColor,
20
+ resolvedOnAdd,
21
+ floatingAddIconColor,
22
+ shouldRenderFloatingAddButton,
23
+ closeCreateForm,
24
+ isCreateFormOpen,
25
+ resolvedActions,
26
+ columnsForTable,
27
+ getOptionsForColumn,
28
+ handleDefaultCreateSaved,
29
+ requestParamsSeed
30
+ }) {
31
+ return (
32
+ <View
33
+ style={[
34
+ styles.wrap,
35
+ {
36
+ borderWidth: tablePanelBorderColor ? 1 : 0,
37
+ borderColor: tablePanelBorderColor,
38
+ backgroundColor: themeColors.panelBackground,
39
+ },
40
+ ]}
41
+ >
42
+ {showToolbar !== false ? (
43
+ <DefaultTableToolbar
44
+ storeName={storeName}
45
+ showToolbarActions={showToolbarActions}
46
+ showToolbarControls={showToolbarControls}
47
+ />
48
+ ) : null}
49
+ <DefaultTableBody storeName={storeName} />
50
+ <DefaultTableFooter storeName={storeName} />
51
+ {shouldRenderBottomAddButton ? (
52
+ <View style={styles.bottomAddBar}>
53
+ <TouchableOpacity
54
+ accessibilityRole="button"
55
+ accessibilityLabel={resolvedAddLabel}
56
+ activeOpacity={0.84}
57
+ style={[
58
+ styles.bottomAddButton,
59
+ { backgroundColor: floatingAddBackgroundColor },
60
+ ]}
61
+ onPress={resolvedOnAdd}
62
+ >
63
+ <Icon name="plus" size={18} color={floatingAddIconColor} />
64
+ <Text style={[styles.bottomAddText, { color: floatingAddIconColor }]}>
65
+ {resolvedAddLabel}
66
+ </Text>
67
+ </TouchableOpacity>
68
+ </View>
69
+ ) : null}
70
+ {shouldRenderFloatingAddButton ? (
71
+ <TouchableOpacity
72
+ accessibilityRole="button"
73
+ accessibilityLabel={resolvedAddLabel}
74
+ activeOpacity={0.84}
75
+ style={[
76
+ styles.floatingAddButton,
77
+ { backgroundColor: floatingAddBackgroundColor },
78
+ ]}
79
+ onPress={resolvedOnAdd}
80
+ >
81
+ <Icon name="plus" size={24} color={floatingAddIconColor} />
82
+ </TouchableOpacity>
83
+ ) : null}
84
+ <Modal
85
+ animationType="fade"
86
+ onRequestClose={closeCreateForm}
87
+ transparent
88
+ visible={isCreateFormOpen}
89
+ >
90
+ <View style={styles.modalOverlay}>
91
+ <View style={styles.modalCard}>
92
+ <View style={styles.modalHeader}>
93
+ <Text style={styles.modalTitle}>{resolvedAddLabel}</Text>
94
+ <TouchableOpacity
95
+ accessibilityRole="button"
96
+ accessibilityLabel={global.t?.t(storeName, 'button', 'cancel') || 'Cancelar'}
97
+ onPress={closeCreateForm}
98
+ style={styles.modalCloseButton}
99
+ >
100
+ <Icon name="x" size={16} color={themeColors.textPrimary || '#0F172A'} />
101
+ </TouchableOpacity>
102
+ </View>
103
+ <DefaultForm
104
+ actions={resolvedActions}
105
+ columns={columnsForTable}
106
+ getOptionsForColumn={getOptionsForColumn}
107
+ mode="create"
108
+ onCancel={closeCreateForm}
109
+ onSaved={handleDefaultCreateSaved}
110
+ row={requestParamsSeed}
111
+ storeName={storeName}
112
+ />
113
+ </View>
114
+ </View>
115
+ </Modal>
116
+ </View>
117
+ );
118
+ }
@@ -287,6 +287,43 @@ describe('DefaultTable', () => {
287
287
  }
288
288
  });
289
289
 
290
+ it('can show only search in the toolbar while hiding configured actions and controls', () => {
291
+ mockWindowDimensions = {width: 375, height: 667};
292
+ mockStores.categories = {
293
+ actions: {},
294
+ getters: {
295
+ columns: [{key: 'name', label: 'Nome', searchable: true}],
296
+ configs: {
297
+ import: {enabled: true, importType: 'product', label: 'Importar CSV'},
298
+ showSearch: true,
299
+ },
300
+ items: [],
301
+ totalItems: 17,
302
+ },
303
+ };
304
+ let tree;
305
+
306
+ renderer.act(() => {
307
+ tree = renderer.create(
308
+ React.createElement(DefaultTable, {
309
+ columns: [{key: 'name', label: 'Nome', searchable: true}],
310
+ data: [],
311
+ showToolbarActions: false,
312
+ showToolbarControls: false,
313
+ showTotalItemsInFooter: false,
314
+ storeName: 'categories',
315
+ }),
316
+ );
317
+ });
318
+
319
+ const iconNames = tree.root.findAllByType('icon').map(node => node.props.name);
320
+ expect(iconNames).toContain('search');
321
+ expect(iconNames).not.toEqual(
322
+ expect.arrayContaining(['upload', 'code', 'filter', 'list', 'columns']),
323
+ );
324
+ expect(tree.root.findAllByType('Text').map(node => node.props.children)).not.toContain('Importar CSV');
325
+ });
326
+
290
327
  it('keeps the list/card toggle visible on compact layouts even when cards are forced', () => {
291
328
  let tree;
292
329
 
@@ -0,0 +1,42 @@
1
+ const React = require('react');
2
+ const renderer = require('react-test-renderer');
3
+ global.IS_REACT_ACT_ENVIRONMENT = true;
4
+ let mockConfigs;
5
+ jest.mock('@store', () => ({useStore: () => ({getters: {configs: mockConfigs}})}));
6
+ jest.mock('react-native', () => ({
7
+ View: props => require('react').createElement('view', props, props.children),
8
+ Text: props => require('react').createElement('text', props, props.children),
9
+ StyleSheet: {create: value => value},
10
+ Platform: {select: value => value.web || value.default || null},
11
+ useWindowDimensions: () => ({width: 400}),
12
+ }));
13
+ jest.mock('../../../../react/components/table/DefaultTableControls', () => () => require('react').createElement('controls'));
14
+ jest.mock('../../../../react/components/table/DefaultToolbarAction', () => props => require('react').createElement('action', props));
15
+ jest.mock('../../../../react/components/table/DefaultTableImportModal', () => () => null);
16
+ jest.mock('../../../../react/components/table/DefaultTableSearch', () => ({
17
+ DefaultTableCollapsedSearch: () => null, DefaultTableCompactSearch: () => null, DefaultTableInlineSearch: () => require('react').createElement('search'),
18
+ }));
19
+ jest.mock('../../../../react/components/table/useDefaultTableTheme', () => () => ({tableBorderColors: {}, toolbarColors: {}}));
20
+ const Toolbar = require('../../../../react/components/table/DefaultTableToolbar').default;
21
+ let tree;
22
+ beforeEach(() => {mockConfigs = {toolbarActions: [{key: 'custom', onPress: jest.fn()}], importAction: jest.fn(), exportAction: jest.fn()};});
23
+ afterEach(() => {if(tree) renderer.act(() => tree.unmount());});
24
+ it('keeps generic actions and controls enabled by default', () => {
25
+ renderer.act(() => {tree = renderer.create(React.createElement(Toolbar, {storeName: 'categories'}));});
26
+ expect(tree.root.findAllByType('action')).toHaveLength(3);
27
+ expect(tree.root.findAllByType('controls')).toHaveLength(1);
28
+ });
29
+ it('hides actions and administrative controls while keeping waiter search', () => {
30
+ renderer.act(() => {tree = renderer.create(React.createElement(Toolbar, {storeName: 'categories', showToolbarActions: false, showToolbarControls: false}));});
31
+ expect(tree.root.findAllByType('action')).toHaveLength(0);
32
+ expect(tree.root.findAllByType('controls')).toHaveLength(0);
33
+ expect(tree.root.findAllByType('search')).toHaveLength(1);
34
+ });
35
+ it('honors stored flags and allows an explicit screen override', () => {
36
+ mockConfigs.showToolbarActions = false; mockConfigs.showToolbarControls = false;
37
+ renderer.act(() => {tree = renderer.create(React.createElement(Toolbar, {storeName: 'categories'}));});
38
+ expect(tree.root.findAllByType('controls')).toHaveLength(0);
39
+ renderer.act(() => tree.update(React.createElement(Toolbar, {storeName: 'categories', showToolbarActions: true, showToolbarControls: true})));
40
+ expect(tree.root.findAllByType('controls')).toHaveLength(1);
41
+ expect(tree.root.findAllByType('action')).toHaveLength(3);
42
+ });