@controleonline/ui-default 1.0.277 → 1.0.279

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.
Files changed (37) hide show
  1. package/docs/compact-order-history-1047.md +25 -0
  2. package/package.json +1 -1
  3. package/src/react/components/filters/CompactFilterSelector.js +194 -185
  4. package/src/react/components/filters/DefaultColumnFilter.js +8 -6
  5. package/src/react/components/filters/DefaultCompactFilterPicker.js +35 -0
  6. package/src/react/components/table/DefaultColumnMenu.js +36 -2
  7. package/src/react/components/table/DefaultCompactDialog.js +36 -0
  8. package/src/react/components/table/DefaultDebug.js +199 -197
  9. package/src/react/components/table/DefaultFiltersModal.js +44 -12
  10. package/src/react/components/table/DefaultTable.js +27 -27
  11. package/src/react/components/table/DefaultTableCards.js +44 -10
  12. package/src/react/components/table/DefaultTableCompact.helpers.js +26 -0
  13. package/src/react/components/table/DefaultTableCompact.styles.js +28 -0
  14. package/src/react/components/table/DefaultTableCompactToolbar.js +112 -0
  15. package/src/react/components/table/DefaultTableInput.js +30 -11
  16. package/src/react/components/table/DefaultTablePaginationBar.js +38 -0
  17. package/src/react/components/table/DefaultTableRows.js +17 -9
  18. package/src/react/components/table/DefaultTableToolbar.js +7 -0
  19. package/src/react/components/table/DefaultTableView.js +5 -2
  20. package/src/react/components/table/useDefaultTableCreateForm.js +29 -0
  21. package/src/react/components/table/useDefaultTablePagination.js +15 -1
  22. package/src/react/components/table/useDefaultTableRowInteraction.js +19 -0
  23. package/src/react/components/table/useDefaultTableStoreSync.js +30 -21
  24. package/src/react/components/table/useDefaultTableTheme.js +44 -41
  25. package/src/tests/react/components/table/DefaultTable.group1.test.js +441 -0
  26. package/src/tests/react/components/table/DefaultTable.group2.test.js +453 -0
  27. package/src/tests/react/components/table/DefaultTable.group3.test.js +289 -0
  28. package/src/tests/react/components/table/DefaultTable.test.js +1 -1381
  29. package/src/tests/react/components/table/DefaultTableToolbar.waiter.test.js +1 -0
  30. package/src/tests/react/components/table/compactDialogs.test.js +53 -0
  31. package/src/tests/react/components/table/compactFilters.test.js +47 -0
  32. package/src/tests/react/components/table/compactPagination.test.js +66 -0
  33. package/src/tests/react/components/table/compactTheme.test.js +34 -0
  34. package/src/tests/react/components/table/compactToolbar.test.js +41 -0
  35. package/src/tests/react/components/table/defaultTableStoreHarness.cjs +35 -0
  36. package/src/tests/react/components/table/defaultTableTestSetup.cjs +207 -0
  37. package/src/tests/react/components/table/useDefaultTableStoreSync.test.js +38 -1
@@ -1,155 +1,4 @@
1
- const React = require('react');
2
- const renderer = require('react-test-renderer');
3
-
4
- const {afterEach, beforeEach, describe, expect, it} = global;
5
-
6
- global.IS_REACT_ACT_ENVIRONMENT = true;
7
-
8
- let consoleErrorSpy = null;
9
- let mockStores = {};
10
- let mockWindowDimensions = {width: 480, height: 800};
11
- let mockCapturedColumnFilterProps = [];
12
- let mockCapturedDefaultFormProps = [];
13
- let mockCapturedDefaultInputProps = [];
14
-
15
- const createLocalStorageMock = () => {
16
- let storage = {};
17
-
18
- return {
19
- clear: () => {
20
- storage = {};
21
- },
22
- getItem: key => (key in storage ? storage[key] : null),
23
- removeItem: key => {
24
- delete storage[key];
25
- },
26
- setItem: (key, value) => {
27
- storage[key] = String(value);
28
- },
29
- };
30
- };
31
-
32
- global.localStorage = createLocalStorageMock();
33
-
34
- jest.mock('@store', () => ({
35
- getAllStores: jest.fn(() => ({})),
36
- useStore: jest.fn(name => mockStores[name] || {actions: {}, getters: {}}),
37
- }));
38
-
39
- jest.mock('@react-navigation/native', () => ({
40
- NavigationRouteContext: require('react').createContext(undefined),
41
- useIsFocused: jest.fn(() => false),
42
- useRoute: jest.fn(() => ({
43
- key: 'OrderHistoryPage-key',
44
- name: 'OrderHistoryPage',
45
- })),
46
- }));
47
-
48
- jest.mock('@controleonline/ui-common/src/react/components/MessageService', () => ({
49
- useMessage: jest.fn(() => ({
50
- showError: jest.fn(),
51
- })),
52
- }));
53
-
54
- jest.mock('react-native-vector-icons/Feather', () => {
55
- const React = require('react');
56
- return props => React.createElement('icon', props, props.children);
57
- });
58
-
59
- jest.mock('react-native', () => {
60
- const React = require('react');
61
- const createComponent = name => props =>
62
- React.createElement(name, props, props.children);
63
-
64
- const FlatList = props => {
65
- const items = Array.isArray(props.data) ? props.data : [];
66
-
67
- return React.createElement(
68
- 'FlatList',
69
- props,
70
- items.map((item, index) =>
71
- props.renderItem ? props.renderItem({item, index}) : null,
72
- ),
73
- items.length === 0 && props.ListEmptyComponent ? props.ListEmptyComponent : null,
74
- props.ListFooterComponent || null,
75
- );
76
- };
77
-
78
- return {
79
- FlatList,
80
- Modal: createComponent('Modal'),
81
- Platform: {
82
- select: value => value.web || value.default || null,
83
- },
84
- ScrollView: createComponent('ScrollView'),
85
- StyleSheet: {create: value => value},
86
- Text: createComponent('Text'),
87
- TouchableOpacity: createComponent('TouchableOpacity'),
88
- View: createComponent('View'),
89
- useWindowDimensions: jest.fn(() => mockWindowDimensions),
90
- };
91
- });
92
-
93
- jest.mock('../../../../react/components/filters/DefaultColumnFilter', () => props => {
94
- const React = require('react');
95
- mockCapturedColumnFilterProps.push(props);
96
- return React.createElement('DefaultColumnFilter', props);
97
- });
98
-
99
- jest.mock('../../../../react/components/filters/DefaultSearch', () => props => {
100
- const React = require('react');
101
- return React.createElement('DefaultSearch', props);
102
- });
103
-
104
- jest.mock('../../../../react/components/table/DefaultTableImportModal', () => props => {
105
- const React = require('react');
106
- return React.createElement('DefaultTableImportModal', props);
107
- });
108
-
109
- jest.mock('../../../../react/components/form/DefaultForm', () => props => {
110
- const React = require('react');
111
- mockCapturedDefaultFormProps.push(props);
112
- return React.createElement('DefaultForm', props);
113
- });
114
-
115
- jest.mock('../../../../react/components/inputs/DefaultInput', () => props => {
116
- const React = require('react');
117
- mockCapturedDefaultInputProps.push(props);
118
- return React.createElement('DefaultInput', props);
119
- });
120
-
121
- jest.mock('@controleonline/ui-common/src/react/components/StateStore', () => props => {
122
- const React = require('react');
123
- return React.createElement('StateStore', props, props.children);
124
- });
125
-
126
- const {
127
- default: DefaultTable,
128
- mergeSortedDataWithLiveItems,
129
- resolveColumnListLoadParams,
130
- shouldTriggerEndReachedFromScroll,
131
- } = require('../../../../react/components/table/DefaultTable');
132
- const {
133
- DEFAULT_TABLE_PREFERENCES_STORAGE_KEY,
134
- } = require('../../../../react/utils/tableVisibleColumnsPreferences');
135
- const reactNavigation = require('@react-navigation/native');
136
- const {getAllStores} = require('@store');
137
- const STORE_ACTION_META_KEY = '__storeMeta';
138
-
139
- const flattenStyle = style => {
140
- if (Array.isArray(style)) {
141
- return style.reduce(
142
- (acc, item) => ({
143
- ...acc,
144
- ...flattenStyle(item),
145
- }),
146
- {},
147
- );
148
- }
149
-
150
- return style && typeof style === 'object' ? style : {};
151
- };
152
-
1
+ const {registerDefaultTableTests, React, renderer, mockState, DefaultTable, mergeSortedDataWithLiveItems, resolveColumnListLoadParams, shouldTriggerEndReachedFromScroll, DEFAULT_TABLE_PREFERENCES_STORAGE_KEY, reactNavigation, getAllStores, STORE_ACTION_META_KEY, flattenStyle} = require('./defaultTableTestSetup.cjs');
153
2
  describe('resolveColumnListLoadParams', () => {
154
3
  it('uses company for category stores and people for financial owner stores', () => {
155
4
  expect(resolveColumnListLoadParams({
@@ -177,7 +26,6 @@ describe('resolveColumnListLoadParams', () => {
177
26
  })).toEqual({company: 21, context: 'receive'});
178
27
  });
179
28
  });
180
-
181
29
  describe('mergeSortedDataWithLiveItems', () => {
182
30
  it('keeps the sorted order and replaces stale rows with live store items', () => {
183
31
  const sortedData = [
@@ -213,7 +61,6 @@ describe('mergeSortedDataWithLiveItems', () => {
213
61
  expect(mergedData[0].status.status).toBe('paid');
214
62
  });
215
63
  });
216
-
217
64
  describe('shouldTriggerEndReachedFromScroll', () => {
218
65
  it('returns true before the scroll reaches the exact end', () => {
219
66
  expect(shouldTriggerEndReachedFromScroll({
@@ -233,1230 +80,3 @@ describe('shouldTriggerEndReachedFromScroll', () => {
233
80
  })).toBe(false);
234
81
  });
235
82
  });
236
-
237
- describe('DefaultTable', () => {
238
- beforeEach(() => {
239
- consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
240
- mockWindowDimensions = {width: 480, height: 800};
241
- mockCapturedColumnFilterProps = [];
242
- mockCapturedDefaultFormProps = [];
243
- mockCapturedDefaultInputProps = [];
244
- getAllStores.mockImplementation(() => ({}));
245
- global.localStorage = createLocalStorageMock();
246
- global.t = {
247
- t: jest.fn((store, type, key) => {
248
- if (type === 'label' && key === 'search') return 'Buscar';
249
- if (type === 'label' && key === 'items') return 'registros';
250
- if (type === 'label' && key === 'loading') return 'Carregando...';
251
- if (type === 'label' && key === 'empty') return 'Nenhum registro encontrado';
252
- if (type === 'label' && key === 'filters') return 'Filtros';
253
- if (type === 'label' && key === 'select') return 'Selecionar';
254
- if (type === 'button' && key === 'add') return 'Adicionar';
255
- if (type === 'button' && key === 'edit') return 'Editar';
256
- if (type === 'button' && key === 'apply') return 'Aplicar';
257
- if (type === 'button' && key === 'clear') return 'Limpar';
258
- if (type === 'input' && key === 'search') return 'Buscar...';
259
- return undefined;
260
- }),
261
- };
262
- reactNavigation.useIsFocused.mockImplementation(() => false);
263
-
264
- mockStores = {
265
- people: {
266
- getters: {
267
- currentCompany: {
268
- theme: {
269
- colors: {},
270
- },
271
- },
272
- },
273
- },
274
- theme: {
275
- getters: {
276
- colors: {},
277
- },
278
- },
279
- };
280
- });
281
-
282
- afterEach(() => {
283
- delete global.t;
284
- if (consoleErrorSpy) {
285
- consoleErrorSpy.mockRestore();
286
- consoleErrorSpy = null;
287
- }
288
- });
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
-
327
- it('keeps the list/card toggle visible on compact layouts even when cards are forced', () => {
328
- let tree;
329
-
330
- renderer.act(() => {
331
- tree = renderer.create(
332
- React.createElement(DefaultTable, {
333
- columns: [{key: 'name', label: 'Nome'}],
334
- data: [],
335
- showColumnFiltersButton: false,
336
- }),
337
- );
338
- });
339
-
340
- const iconNames = tree.root.findAllByType('icon').map(node => node.props.name);
341
-
342
- expect(iconNames).toEqual(expect.arrayContaining(['grid', 'columns']));
343
- });
344
-
345
- it('respects the compact grid/list toggle in the table body', () => {
346
- mockWindowDimensions = {width: 480, height: 800};
347
- mockStores.orders = {
348
- actions: {},
349
- getters: {
350
- columns: [{key: 'name', label: 'Nome'}],
351
- items: [{id: 1, name: 'Pedido'}],
352
- visibleColumns: {},
353
- },
354
- };
355
- let tree;
356
-
357
- renderer.act(() => {
358
- tree = renderer.create(
359
- React.createElement(DefaultTable, {
360
- data: [{id: 1, name: 'Pedido'}],
361
- showColumnFiltersButton: false,
362
- storeName: 'orders',
363
- }),
364
- );
365
- });
366
-
367
- expect(
368
- tree.root.findAllByType('ScrollView').some(node => node.props.horizontal === true),
369
- ).toBe(false);
370
-
371
- const toggleViewButton = tree.root
372
- .findAllByType('TouchableOpacity')
373
- .find(node => node.findAllByType('icon').some(icon => icon.props.name === 'list'));
374
-
375
- renderer.act(() => {
376
- toggleViewButton.props.onPress();
377
- tree.update(
378
- React.createElement(DefaultTable, {
379
- data: [{id: 1, name: 'Pedido'}],
380
- showColumnFiltersButton: false,
381
- storeName: 'orders',
382
- }),
383
- );
384
- });
385
-
386
- expect(
387
- tree.root.findAllByType('ScrollView').some(node => node.props.horizontal === true),
388
- ).toBe(true);
389
- expect(tree.root.findAllByType('icon').map(node => node.props.name)).toContain('grid');
390
- });
391
-
392
- it('pins identity and action columns on table view', () => {
393
- mockWindowDimensions = {width: 1200, height: 800};
394
- mockStores.orders = {
395
- actions: {},
396
- getters: {
397
- columns: [
398
- {key: 'id', label: 'ID', isIdentity: true},
399
- {key: 'name', label: 'Nome'},
400
- ],
401
- items: [{id: 1, name: 'Pedido'}],
402
- visibleColumns: {},
403
- },
404
- };
405
- const rowActionsComponent = () => React.createElement('row-actions');
406
- let tree;
407
-
408
- renderer.act(() => {
409
- tree = renderer.create(
410
- React.createElement(DefaultTable, {
411
- rowActionsComponent,
412
- storeName: 'orders',
413
- }),
414
- );
415
- });
416
-
417
- const renderedStyles = [
418
- ...tree.root.findAllByType('View'),
419
- ...tree.root.findAllByType('TouchableOpacity'),
420
- ].map(node => flattenStyle(node.props.style));
421
-
422
- expect(renderedStyles).toEqual(
423
- expect.arrayContaining([
424
- expect.objectContaining({position: 'sticky', left: 0}),
425
- expect.objectContaining({position: 'sticky', right: 0}),
426
- ]),
427
- );
428
-
429
- const horizontalScroll = tree.root
430
- .findAllByType('ScrollView')
431
- .find(node => node.props.horizontal);
432
-
433
- renderer.act(() => {
434
- horizontalScroll.props.onLayout({nativeEvent: {layout: {width: 300}}});
435
- horizontalScroll.props.onContentSizeChange(600, 200);
436
- horizontalScroll.props.onScroll({nativeEvent: {contentOffset: {x: 120}}});
437
- });
438
-
439
- const scrolledStyles = [
440
- ...tree.root.findAllByType('View'),
441
- ...tree.root.findAllByType('TouchableOpacity'),
442
- ].map(node => flattenStyle(node.props.style));
443
-
444
- expect(scrolledStyles).toEqual(
445
- expect.arrayContaining([
446
- expect.objectContaining({transform: [{translateX: 120}]}),
447
- expect.objectContaining({transform: [{translateX: -180}]}),
448
- ]),
449
- );
450
- });
451
-
452
- it('renders a custom row actions component with the configured actions width', () => {
453
- mockWindowDimensions = {width: 1200, height: 800};
454
- mockStores.orders = {
455
- actions: {},
456
- getters: {
457
- columns: [
458
- {key: 'id', label: 'ID', isIdentity: true},
459
- {key: 'name', label: 'Nome'},
460
- ],
461
- items: [{id: 12, name: 'Pedido'}],
462
- visibleColumns: {},
463
- },
464
- };
465
- const rowActionsComponent = props =>
466
- React.createElement('row-actions', props, props.row.name);
467
- let tree;
468
-
469
- renderer.act(() => {
470
- tree = renderer.create(
471
- React.createElement(DefaultTable, {
472
- onEditRow: jest.fn(),
473
- rowActionsComponent,
474
- rowActionsWidth: 180,
475
- storeName: 'orders',
476
- }),
477
- );
478
- });
479
-
480
- const rowActions = tree.root.findByType('row-actions');
481
- expect(rowActions.props.row).toEqual({id: 12, name: 'Pedido'});
482
- expect(rowActions.props.storeName).toBe('orders');
483
- expect(typeof rowActions.props.openEdit).toBe('function');
484
- expect(rowActions.props.helpers).toEqual(
485
- expect.objectContaining({
486
- openEdit: expect.any(Function),
487
- }),
488
- );
489
-
490
- const renderedStyles = [
491
- ...tree.root.findAllByType('View'),
492
- ...tree.root.findAllByType('TouchableOpacity'),
493
- ].map(node => flattenStyle(node.props.style));
494
-
495
- expect(renderedStyles).toEqual(
496
- expect.arrayContaining([
497
- expect.objectContaining({
498
- flexBasis: 180,
499
- maxWidth: 180,
500
- minWidth: 180,
501
- width: 180,
502
- }),
503
- ]),
504
- );
505
- });
506
-
507
- it('keeps only the identity column pinned when row actions are not pinned', () => {
508
- mockWindowDimensions = {width: 1200, height: 800};
509
- mockStores.orders = {
510
- actions: {},
511
- getters: {
512
- columns: [
513
- {key: 'id', label: 'ID', isIdentity: true},
514
- {key: 'name', label: 'Nome'},
515
- ],
516
- items: [{id: 1, name: 'Pedido'}],
517
- visibleColumns: {},
518
- },
519
- };
520
- const rowActionsComponent = () => React.createElement('row-actions');
521
- let tree;
522
-
523
- renderer.act(() => {
524
- tree = renderer.create(
525
- React.createElement(DefaultTable, {
526
- pinRowActions: false,
527
- rowActionsComponent,
528
- storeName: 'orders',
529
- }),
530
- );
531
- });
532
-
533
- const renderedStyles = [
534
- ...tree.root.findAllByType('View'),
535
- ...tree.root.findAllByType('TouchableOpacity'),
536
- ].map(node => flattenStyle(node.props.style));
537
-
538
- expect(renderedStyles).toEqual(
539
- expect.arrayContaining([
540
- expect.objectContaining({position: 'sticky', left: 0}),
541
- ]),
542
- );
543
- expect(renderedStyles).not.toEqual(
544
- expect.arrayContaining([
545
- expect.objectContaining({position: 'sticky', right: 0}),
546
- ]),
547
- );
548
-
549
- const horizontalScroll = tree.root
550
- .findAllByType('ScrollView')
551
- .find(node => node.props.horizontal);
552
-
553
- renderer.act(() => {
554
- horizontalScroll.props.onLayout({nativeEvent: {layout: {width: 300}}});
555
- horizontalScroll.props.onContentSizeChange(600, 200);
556
- horizontalScroll.props.onScroll({nativeEvent: {contentOffset: {x: 120}}});
557
- });
558
-
559
- const scrolledStyles = [
560
- ...tree.root.findAllByType('View'),
561
- ...tree.root.findAllByType('TouchableOpacity'),
562
- ].map(node => flattenStyle(node.props.style));
563
-
564
- expect(scrolledStyles).toEqual(
565
- expect.arrayContaining([
566
- expect.objectContaining({transform: [{translateX: 120}]}),
567
- ]),
568
- );
569
- expect(scrolledStyles).not.toEqual(
570
- expect.arrayContaining([
571
- expect.objectContaining({transform: [{translateX: -180}]}),
572
- ]),
573
- );
574
- });
575
-
576
- it('keeps the complete search aligned in the toolbar when there is enough room', () => {
577
- mockWindowDimensions = {width: 430, height: 932};
578
- mockStores.orders = {
579
- actions: {},
580
- getters: {
581
- columns: [{key: 'name', label: 'Nome', searchable: true}],
582
- },
583
- };
584
- let tree;
585
-
586
- renderer.act(() => {
587
- tree = renderer.create(
588
- React.createElement(DefaultTable, {
589
- columns: [{key: 'name', label: 'Nome'}],
590
- data: [],
591
- storeName: 'orders',
592
- }),
593
- );
594
- });
595
-
596
- expect(tree.root.findAllByType('DefaultSearch')).toHaveLength(1);
597
- expect(tree.root.findAllByType('icon').map(node => node.props.name)).not.toContain('search');
598
- });
599
-
600
- it('hides the toolbar when the screen disables it', () => {
601
- mockWindowDimensions = {width: 430, height: 932};
602
- mockStores.orders = {
603
- actions: {},
604
- getters: {
605
- columns: [{key: 'name', label: 'Nome', searchable: true}],
606
- },
607
- };
608
- let tree;
609
-
610
- renderer.act(() => {
611
- tree = renderer.create(
612
- React.createElement(DefaultTable, {
613
- columns: [{key: 'name', label: 'Nome', searchable: true}],
614
- data: [],
615
- showToolbar: false,
616
- storeName: 'orders',
617
- }),
618
- );
619
- });
620
-
621
- expect(tree.root.findAllByType('DefaultSearch')).toHaveLength(0);
622
- expect(tree.root.findAllByType('icon').map(node => node.props.name)).not.toEqual(
623
- expect.arrayContaining(['columns', 'grid', 'list', 'search']),
624
- );
625
- });
626
-
627
- it('keeps add available as a floating button when the toolbar is hidden', () => {
628
- const onAdd = jest.fn();
629
- mockWindowDimensions = {width: 430, height: 932};
630
- mockStores.orders = {
631
- actions: {},
632
- getters: {
633
- add: true,
634
- columns: [{key: 'name', label: 'Nome', searchable: true}],
635
- },
636
- };
637
- let tree;
638
-
639
- renderer.act(() => {
640
- tree = renderer.create(
641
- React.createElement(DefaultTable, {
642
- columns: [{key: 'name', label: 'Nome', searchable: true}],
643
- data: [],
644
- onAdd,
645
- showToolbar: false,
646
- storeName: 'orders',
647
- }),
648
- );
649
- });
650
-
651
- const addButton = tree.root
652
- .findAllByType('TouchableOpacity')
653
- .find(node => node.findAllByType('icon').some(icon => icon.props.name === 'plus'));
654
-
655
- expect(addButton).toBeTruthy();
656
- expect(addButton.props.accessibilityRole).toBe('button');
657
- expect(addButton.props.accessibilityLabel).toBe('Adicionar');
658
-
659
- renderer.act(() => addButton.props.onPress());
660
-
661
- expect(onAdd).toHaveBeenCalledTimes(1);
662
- });
663
-
664
- it('opens the default create form when add has no custom handler', () => {
665
- mockWindowDimensions = {width: 430, height: 932};
666
- const save = jest.fn(() => Promise.resolve({id: 10}));
667
- mockStores.invoice = {
668
- actions: {save},
669
- getters: {
670
- add: true,
671
- columns: [{key: 'payer', label: 'Pagador', editable: true}],
672
- },
673
- };
674
- let tree;
675
-
676
- renderer.act(() => {
677
- tree = renderer.create(
678
- React.createElement(DefaultTable, {
679
- requestParams: {receiver: 21},
680
- showToolbar: false,
681
- storeName: 'invoice',
682
- }),
683
- );
684
- });
685
-
686
- const addButton = tree.root
687
- .findAllByType('TouchableOpacity')
688
- .find(node => node.findAllByType('icon').some(icon => icon.props.name === 'plus'));
689
-
690
- expect(tree.root.findAllByType('Modal').some(node => node.props.visible)).toBe(false);
691
-
692
- renderer.act(() => addButton.props.onPress());
693
-
694
- expect(tree.root.findAllByType('Modal').some(node => node.props.visible)).toBe(true);
695
- expect(mockCapturedDefaultFormProps[mockCapturedDefaultFormProps.length - 1]).toEqual(
696
- expect.objectContaining({
697
- actions: expect.objectContaining({save}),
698
- mode: 'create',
699
- row: {receiver: 21},
700
- storeName: 'invoice',
701
- }),
702
- );
703
- });
704
-
705
- it('collapses search into an icon on narrow toolbars and opens the search modal', () => {
706
- mockWindowDimensions = {width: 375, height: 667};
707
- mockStores.orders = {
708
- actions: {},
709
- getters: {
710
- columns: [{key: 'name', label: 'Nome', searchable: true}],
711
- },
712
- };
713
- let tree;
714
-
715
- renderer.act(() => {
716
- tree = renderer.create(
717
- React.createElement(DefaultTable, {
718
- columns: [{key: 'name', label: 'Nome'}],
719
- data: [],
720
- storeName: 'orders',
721
- }),
722
- );
723
- });
724
-
725
- expect(tree.root.findAllByType('DefaultSearch')).toHaveLength(0);
726
- const searchButton = tree.root
727
- .findAllByType('TouchableOpacity')
728
- .find(node => node.findAllByType('icon').some(icon => icon.props.name === 'search'));
729
-
730
- expect(searchButton.props.accessibilityRole).toBe('button');
731
- expect(searchButton.props.accessibilityLabel).toBe('Buscar');
732
-
733
- renderer.act(() => searchButton.props.onPress());
734
-
735
- expect(tree.root.findAllByType('DefaultSearch')).toHaveLength(1);
736
- expect(tree.root.findByType('DefaultSearch').props.autoFocus).toBe(true);
737
- });
738
-
739
- it('gives collapsed search a stable accessible name without a placeholder', () => {
740
- mockWindowDimensions = {width: 375, height: 667};
741
- mockStores.orders = {
742
- actions: {},
743
- getters: {
744
- columns: [{key: 'name', label: 'Nome', searchable: true}],
745
- },
746
- };
747
- let tree;
748
-
749
- renderer.act(() => {
750
- tree = renderer.create(
751
- React.createElement(DefaultTable, {
752
- columns: [{key: 'name', label: 'Nome'}],
753
- data: [],
754
- storeName: 'orders',
755
- }),
756
- );
757
- });
758
-
759
- const searchButton = tree.root
760
- .findAllByType('TouchableOpacity')
761
- .find(node => node.findAllByType('icon').some(icon => icon.props.name === 'search'));
762
-
763
- expect(searchButton.props.accessibilityRole).toBe('button');
764
- expect(searchButton.props.accessibilityLabel).toBe('Buscar');
765
- });
766
-
767
- it('keeps the financial compact toolbar aligned when the total is already in the footer', () => {
768
- mockWindowDimensions = {width: 375, height: 667};
769
- mockStores.orders = {
770
- actions: {},
771
- getters: {
772
- columns: [{key: 'name', label: 'Nome', searchable: true}],
773
- totalItems: 27,
774
- },
775
- };
776
- let tree;
777
-
778
- renderer.act(() => {
779
- tree = renderer.create(
780
- React.createElement(DefaultTable, {
781
- columns: [{key: 'name', label: 'Nome'}],
782
- data: [],
783
- showTotalItemsInCompactToolbar: true,
784
- showTotalItemsInFooter: true,
785
- storeName: 'orders',
786
- }),
787
- );
788
- });
789
-
790
- const iconNames = tree.root.findAllByType('icon').map(node => node.props.name);
791
-
792
- expect(tree.root.findAllByType('DefaultSearch')).toHaveLength(0);
793
- expect(iconNames).toEqual(expect.arrayContaining(['search', 'filter', 'list', 'columns']));
794
- expect(
795
- tree.root.findAllByType('Text').filter(node => node.props.children === '27 registros'),
796
- ).toHaveLength(1);
797
- });
798
-
799
- it('opens the filters modal with filterable columns only', () => {
800
- mockWindowDimensions = {width: 375, height: 667};
801
- mockStores.orders = {
802
- actions: {},
803
- getters: {
804
- columns: [
805
- {key: 'name', label: 'Nome'},
806
- {key: 'ignored', label: 'Ignorado', filter: false},
807
- {
808
- key: 'status',
809
- label: 'Status',
810
- list: [{value: 'pos', label: 'POS'}],
811
- },
812
- ],
813
- filters: {},
814
- },
815
- };
816
- let tree;
817
-
818
- renderer.act(() => {
819
- tree = renderer.create(
820
- React.createElement(DefaultTable, {
821
- data: [],
822
- storeName: 'orders',
823
- }),
824
- );
825
- });
826
-
827
- expect(tree.root.findAllByType('DefaultColumnFilter')).toHaveLength(0);
828
- const filterButton = tree.root
829
- .findAllByType('TouchableOpacity')
830
- .find(node => node.findAllByType('icon').some(icon => icon.props.name === 'filter'));
831
-
832
- renderer.act(() => filterButton.props.onPress());
833
-
834
- expect(tree.root.findAllByType('DefaultColumnFilter')).toHaveLength(2);
835
- expect(mockCapturedColumnFilterProps.map(props => props.column.key)).toEqual(['name', 'status']);
836
- });
837
-
838
- it('persists filters and sort under default-table[store][route]', () => {
839
- const setFilters = jest.fn();
840
- mockWindowDimensions = {width: 1024, height: 800};
841
- mockStores.orders = {
842
- actions: {
843
- setFilters,
844
- },
845
- getters: {
846
- columns: [
847
- {key: 'status', label: 'Status', sortable: true},
848
- {key: 'price', label: 'Preco'},
849
- ],
850
- filters: {},
851
- },
852
- };
853
-
854
- let tree;
855
-
856
- renderer.act(() => {
857
- tree = renderer.create(
858
- React.createElement(DefaultTable, {
859
- storeName: 'orders',
860
- }),
861
- );
862
- });
863
-
864
- const filterButton = tree.root
865
- .findAllByType('TouchableOpacity')
866
- .find(node => node.findAllByType('icon').some(icon => icon.props.name === 'filter'));
867
-
868
- renderer.act(() => filterButton.props.onPress());
869
-
870
- const statusFilterProps = mockCapturedColumnFilterProps
871
- .find(props => props.column.key === 'status');
872
-
873
- renderer.act(() => {
874
- statusFilterProps.onChange('status', 'paid');
875
- });
876
-
877
- const statusHeader = tree.root
878
- .findAllByType('TouchableOpacity')
879
- .find(node => node.findAllByType('Text').some(text => text.props.children === 'Status'));
880
-
881
- renderer.act(() => {
882
- statusHeader.props.onPress();
883
- });
884
-
885
- expect(setFilters).toHaveBeenCalledWith({status: 'paid'});
886
- expect(
887
- JSON.parse(
888
- global.localStorage.getItem(DEFAULT_TABLE_PREFERENCES_STORAGE_KEY),
889
- ),
890
- ).toEqual({
891
- orders: {
892
- OrderHistoryPage: {
893
- filters: {
894
- status: 'paid',
895
- },
896
- sort: {
897
- direction: 'asc',
898
- field: 'status',
899
- },
900
- },
901
- },
902
- });
903
- });
904
-
905
- it('hydrates the saved sort using a stable preference key on auto tables', async () => {
906
- const getItems = jest.fn(() => Promise.resolve([]));
907
- reactNavigation.useIsFocused.mockImplementation(() => true);
908
- global.localStorage.setItem(
909
- DEFAULT_TABLE_PREFERENCES_STORAGE_KEY,
910
- JSON.stringify({
911
- orders: {
912
- 'order-history-page': {
913
- sort: {
914
- direction: 'desc',
915
- field: 'status',
916
- },
917
- },
918
- },
919
- }),
920
- );
921
- mockStores.orders = {
922
- actions: {
923
- getItems,
924
- },
925
- getters: {
926
- columns: [
927
- {key: 'status', label: 'Status', sortable: true},
928
- {key: 'price', label: 'Preco'},
929
- ],
930
- filters: {},
931
- items: [],
932
- },
933
- };
934
-
935
- await renderer.act(async () => {
936
- renderer.create(
937
- React.createElement(DefaultTable, {
938
- storeName: 'orders',
939
- visibleColumnsPreferenceKey: 'order-history-page',
940
- }),
941
- );
942
- await Promise.resolve();
943
- });
944
-
945
- expect(getItems).toHaveBeenCalledWith(
946
- expect.objectContaining({
947
- 'order[status]': 'desc',
948
- itemsPerPage: 50,
949
- page: 1,
950
- }),
951
- );
952
- });
953
-
954
- it('hydrates and persists controlled filters under default-table[store][route]', () => {
955
- const onFilterChange = jest.fn();
956
- const setFilters = jest.fn();
957
- global.location = {pathname: '/product-showcases-page'};
958
- global.localStorage.setItem(
959
- DEFAULT_TABLE_PREFERENCES_STORAGE_KEY,
960
- JSON.stringify({
961
- product_showcase_items: {
962
- 'product-showcases-page': {
963
- filters: {
964
- integration: 'pos',
965
- },
966
- },
967
- },
968
- }),
969
- );
970
- mockStores.product_showcase_items = {
971
- actions: {
972
- setFilters,
973
- },
974
- getters: {
975
- columns: [
976
- {key: 'integration', label: 'Integration', externalFilter: true},
977
- {key: 'price', label: 'Price'},
978
- ],
979
- filters: {},
980
- },
981
- };
982
-
983
- let tree;
984
-
985
- renderer.act(() => {
986
- tree = renderer.create(
987
- React.createElement(DefaultTable, {
988
- filters: {},
989
- onFilterChange,
990
- storeName: 'product_showcase_items',
991
- }),
992
- );
993
- });
994
-
995
- expect(onFilterChange).toHaveBeenCalledWith({integration: 'pos'});
996
-
997
- renderer.act(() => {
998
- tree.update(
999
- React.createElement(DefaultTable, {
1000
- filters: {integration: 'ifood'},
1001
- onFilterChange,
1002
- storeName: 'product_showcase_items',
1003
- }),
1004
- );
1005
- });
1006
-
1007
- expect(
1008
- JSON.parse(
1009
- global.localStorage.getItem(DEFAULT_TABLE_PREFERENCES_STORAGE_KEY),
1010
- ),
1011
- ).toEqual({
1012
- product_showcase_items: {
1013
- 'product-showcases-page': {
1014
- filters: {
1015
- integration: 'ifood',
1016
- },
1017
- },
1018
- },
1019
- });
1020
-
1021
- delete global.location;
1022
- });
1023
-
1024
- it('preserves the compact toolbar total when the footer total is disabled', () => {
1025
- mockWindowDimensions = {width: 375, height: 667};
1026
- mockStores.orders = {
1027
- actions: {},
1028
- getters: {
1029
- totalItems: 27,
1030
- },
1031
- };
1032
- let tree;
1033
-
1034
- renderer.act(() => {
1035
- tree = renderer.create(
1036
- React.createElement(DefaultTable, {
1037
- columns: [{key: 'name', label: 'Nome'}],
1038
- data: [],
1039
- showTotalItemsInCompactToolbar: true,
1040
- showTotalItemsInFooter: false,
1041
- storeName: 'orders',
1042
- }),
1043
- );
1044
- });
1045
-
1046
- expect(tree.root.findAllByType('Text').map(node => node.props.children)).toContain('27 registros');
1047
- });
1048
-
1049
- it('forces cards when entering compact mode and still toggles between cards and list', () => {
1050
- const props = {
1051
- columns: [{key: 'name', label: 'Nome'}],
1052
- data: [{id: 1, name: 'Pedido 1'}],
1053
- showColumnFiltersButton: false,
1054
- };
1055
- let tree;
1056
-
1057
- mockWindowDimensions = {width: 1024, height: 800};
1058
-
1059
- renderer.act(() => {
1060
- tree = renderer.create(
1061
- React.createElement(DefaultTable, props),
1062
- );
1063
- });
1064
-
1065
- expect(tree.root.findAllByType('ScrollView')).toHaveLength(1);
1066
-
1067
- mockWindowDimensions = {width: 480, height: 800};
1068
-
1069
- renderer.act(() => {
1070
- tree.update(React.createElement(DefaultTable, props));
1071
- });
1072
-
1073
- expect(tree.root.findAllByType('ScrollView')).toHaveLength(0);
1074
- expect(tree.root.findByProps({name: 'grid'})).toBeTruthy();
1075
-
1076
- renderer.act(() => {
1077
- tree.root.findByProps({name: 'grid'}).parent.props.onPress();
1078
- tree.update(React.createElement(DefaultTable, props));
1079
- });
1080
-
1081
- expect(tree.root.findAllByType('ScrollView')).toHaveLength(1);
1082
- expect(tree.root.findByProps({name: 'list'})).toBeTruthy();
1083
-
1084
- renderer.act(() => {
1085
- tree.root.findByProps({name: 'list'}).parent.props.onPress();
1086
- tree.update(React.createElement(DefaultTable, props));
1087
- });
1088
-
1089
- expect(tree.root.findAllByType('ScrollView')).toHaveLength(0);
1090
- expect(tree.root.findByProps({name: 'grid'})).toBeTruthy();
1091
- });
1092
-
1093
- it('applies a custom rowStyle to rendered table rows', () => {
1094
- let tree;
1095
- const rowStyle = jest.fn(() => ({
1096
- borderLeftColor: '#DC2626',
1097
- borderLeftWidth: 4,
1098
- }));
1099
-
1100
- mockWindowDimensions = {width: 1024, height: 800};
1101
-
1102
- renderer.act(() => {
1103
- tree = renderer.create(
1104
- React.createElement(DefaultTable, {
1105
- columns: [{key: 'name', label: 'Nome'}],
1106
- data: [{id: 1, name: 'Pedido 1'}],
1107
- onRowPress: () => {},
1108
- rowStyle,
1109
- showColumnFiltersButton: false,
1110
- }),
1111
- );
1112
- });
1113
-
1114
- const row = tree.root
1115
- .findAllByType('TouchableOpacity')
1116
- .find(node => node.props?.activeOpacity === 0.84);
1117
-
1118
- expect(rowStyle).toHaveBeenCalledWith(
1119
- expect.objectContaining({id: 1, name: 'Pedido 1'}),
1120
- 0,
1121
- );
1122
- expect(Array.isArray(row.props.style)).toBe(true);
1123
- expect(
1124
- row.props.style.some(
1125
- style => style && style.borderLeftWidth === 4 && style.borderLeftColor === '#DC2626',
1126
- ),
1127
- ).toBe(true);
1128
- });
1129
-
1130
- it('blocks row navigation while an inline cell is editing or saving', async () => {
1131
- let resolveSave;
1132
- const onRowPress = jest.fn();
1133
- const save = jest.fn(() => new Promise(resolve => {
1134
- resolveSave = resolve;
1135
- }));
1136
- let tree;
1137
-
1138
- mockWindowDimensions = {width: 1024, height: 800};
1139
-
1140
- renderer.act(() => {
1141
- tree = renderer.create(
1142
- React.createElement(DefaultTable, {
1143
- actions: {save},
1144
- columns: [{key: 'name', label: 'Nome', editable: true}],
1145
- data: [{id: 1, name: 'Cliente'}],
1146
- onRowPress,
1147
- showColumnFiltersButton: false,
1148
- showRowActions: false,
1149
- }),
1150
- );
1151
- });
1152
-
1153
- const findRow = () => tree.root
1154
- .findAllByType('TouchableOpacity')
1155
- .find(node => node.props?.activeOpacity === 0.84);
1156
- const latestInput = () => mockCapturedDefaultInputProps[mockCapturedDefaultInputProps.length - 1];
1157
-
1158
- renderer.act(() => {
1159
- findRow().props.onPress();
1160
- });
1161
- expect(onRowPress).toHaveBeenCalledTimes(1);
1162
- onRowPress.mockClear();
1163
-
1164
- renderer.act(() => {
1165
- latestInput().onStartEditing();
1166
- });
1167
- renderer.act(() => {
1168
- findRow().props.onPress();
1169
- });
1170
- expect(onRowPress).not.toHaveBeenCalled();
1171
-
1172
- let savePromise;
1173
- renderer.act(() => {
1174
- savePromise = latestInput().onSave('Cliente alterado');
1175
- });
1176
- renderer.act(() => {
1177
- latestInput().onCancelEditing();
1178
- });
1179
- renderer.act(() => {
1180
- findRow().props.onPress();
1181
- });
1182
- expect(onRowPress).not.toHaveBeenCalled();
1183
-
1184
- await renderer.act(async () => {
1185
- resolveSave({id: 1, name: 'Cliente alterado'});
1186
- await savePromise;
1187
- });
1188
-
1189
- renderer.act(() => {
1190
- findRow().props.onPress();
1191
- });
1192
- expect(onRowPress).toHaveBeenCalledTimes(1);
1193
- });
1194
-
1195
- it('protects custom card openRow while a card field is editing', () => {
1196
- const onRowPress = jest.fn();
1197
- let tree;
1198
-
1199
- renderer.act(() => {
1200
- tree = renderer.create(
1201
- React.createElement(DefaultTable, {
1202
- columns: [{key: 'name', label: 'Nome', editable: true}],
1203
- data: [{id: 1, name: 'Cliente'}],
1204
- onRowPress,
1205
- renderCard: ({openRow, renderField}) => React.createElement(
1206
- 'CustomCard',
1207
- {openRow},
1208
- renderField('name'),
1209
- ),
1210
- showColumnFiltersButton: false,
1211
- showRowActions: false,
1212
- }),
1213
- );
1214
- });
1215
-
1216
- const latestInput = () => mockCapturedDefaultInputProps[mockCapturedDefaultInputProps.length - 1];
1217
-
1218
- renderer.act(() => {
1219
- latestInput().onStartEditing();
1220
- });
1221
- renderer.act(() => {
1222
- tree.root.findByType('CustomCard').props.openRow();
1223
- });
1224
- expect(onRowPress).not.toHaveBeenCalled();
1225
-
1226
- renderer.act(() => {
1227
- latestInput().onCancelEditing();
1228
- });
1229
- renderer.act(() => {
1230
- tree.root.findByType('CustomCard').props.openRow();
1231
- });
1232
- expect(onRowPress).toHaveBeenCalledTimes(1);
1233
- });
1234
-
1235
- it('renders a custom footer component when provided', () => {
1236
- let tree;
1237
- mockStores.orders = {
1238
- actions: {},
1239
- getters: {
1240
- totalItems: 7,
1241
- },
1242
- };
1243
-
1244
- const FooterComponent = props =>
1245
- React.createElement('CustomFooter', {
1246
- resolvedTotalItemsText: props.resolvedTotalItemsText,
1247
- });
1248
-
1249
- mockWindowDimensions = {width: 1024, height: 800};
1250
-
1251
- renderer.act(() => {
1252
- tree = renderer.create(
1253
- React.createElement(DefaultTable, {
1254
- columns: [{key: 'name', label: 'Nome'}],
1255
- data: [{id: 1, name: 'Pedido 1'}],
1256
- footerComponent: FooterComponent,
1257
- showColumnFiltersButton: false,
1258
- storeName: 'orders',
1259
- }),
1260
- );
1261
- });
1262
-
1263
- const footer = tree.root.findByType('CustomFooter');
1264
-
1265
- expect(footer.props.resolvedTotalItemsText).toBe('7 registros');
1266
- });
1267
-
1268
- it('keeps infinite scroll loading transparent over existing rows', () => {
1269
- let tree;
1270
- const getItems = jest.fn(() => Promise.resolve({member: []}));
1271
- mockWindowDimensions = {width: 1024, height: 800};
1272
- mockStores.invoices = {
1273
- actions: {
1274
- getItems,
1275
- },
1276
- getters: {
1277
- columns: [{key: 'status', label: 'Situação'}],
1278
- items: [{id: 332, status: 'Pago'}],
1279
- totalItems: 1394,
1280
- },
1281
- };
1282
-
1283
- renderer.act(() => {
1284
- tree = renderer.create(
1285
- React.createElement(DefaultTable, {
1286
- showColumnFiltersButton: false,
1287
- showRowActions: false,
1288
- storeName: 'invoices',
1289
- }),
1290
- );
1291
- });
1292
-
1293
- expect(tree.root.findAllByType('StateStore')).toHaveLength(0);
1294
- const flatList = tree.root.findByType('FlatList');
1295
- expect(flatList.props.onEndReachedThreshold).toBeGreaterThanOrEqual(0.75);
1296
- expect(tree.root.findByType('ScrollView').props.contentContainerStyle).toEqual(
1297
- expect.objectContaining({minWidth: '100%'}),
1298
- );
1299
-
1300
- renderer.act(() => {
1301
- flatList.props.onScroll({
1302
- nativeEvent: {
1303
- contentOffset: {y: 650},
1304
- contentSize: {height: 1200},
1305
- layoutMeasurement: {height: 400},
1306
- },
1307
- });
1308
- });
1309
-
1310
- expect(getItems).toHaveBeenCalledWith(expect.objectContaining({append: true}));
1311
- });
1312
-
1313
- it('uses the configured summary and labels before the store summary', () => {
1314
- let tree;
1315
- mockWindowDimensions = {width: 1024, height: 800};
1316
- mockStores.invoice = {
1317
- actions: {},
1318
- getters: {
1319
- summary: {
1320
- financial: {
1321
- paidAmount: 0,
1322
- },
1323
- },
1324
- totalItems: 1,
1325
- },
1326
- };
1327
-
1328
- renderer.act(() => {
1329
- tree = renderer.create(
1330
- React.createElement(DefaultTable, {
1331
- columns: [{key: 'status', label: 'Situação'}],
1332
- data: [{id: 332, status: 'Pago'}],
1333
- showColumnFiltersButton: false,
1334
- showRowActions: false,
1335
- storeName: 'invoice',
1336
- summary: {
1337
- financial: {
1338
- paidAmount: 80.25,
1339
- },
1340
- },
1341
- summaryLabels: {
1342
- 'financial.paidAmount': 'Valor pago',
1343
- },
1344
- }),
1345
- );
1346
- });
1347
-
1348
- const textValues = tree.root.findAllByType('Text').map(node => node.props.children);
1349
- expect(textValues).toContain('Valor pago');
1350
- expect(textValues).toContain('R$ 80,25');
1351
- });
1352
-
1353
- it('opens a debug query modal when the store exposes debug.query', () => {
1354
- mockWindowDimensions = {width: 1024, height: 800};
1355
- mockStores.orders = {
1356
- actions: {},
1357
- getters: {
1358
- columns: [{key: 'id', label: 'ID'}],
1359
- debug: {
1360
- interpolatedQuery: "SELECT * FROM orders WHERE provider_id = '2'",
1361
- query: 'SELECT * FROM orders WHERE provider_id = 2',
1362
- },
1363
- filters: {
1364
- provider: '/people/2',
1365
- },
1366
- items: [],
1367
- },
1368
- };
1369
-
1370
- let tree;
1371
-
1372
- renderer.act(() => {
1373
- tree = renderer.create(
1374
- React.createElement(DefaultTable, {
1375
- requestParams: {itemsPerPage: 50},
1376
- storeName: 'orders',
1377
- }),
1378
- );
1379
- });
1380
-
1381
- const debugButton = tree.root
1382
- .findAllByType('TouchableOpacity')
1383
- .find(node => node.props.accessibilityLabel === 'Debug query');
1384
-
1385
- expect(debugButton).toBeTruthy();
1386
-
1387
- renderer.act(() => debugButton.props.onPress());
1388
-
1389
- const textValues = tree.root.findAllByType('Text').map(node => node.props.children);
1390
- expect(textValues).toContain('Debug query');
1391
- expect(textValues).toContain('SELECT * FROM orders WHERE provider_id = 2');
1392
- expect(textValues).toContain('Query preenchida');
1393
- expect(textValues).toContain("SELECT * FROM orders WHERE provider_id = '2'");
1394
- expect(textValues.some(value => String(value).includes('"provider": "/people/2"'))).toBe(true);
1395
- });
1396
-
1397
- it('uses store actions when no explicit table actions are passed', async () => {
1398
- const save = jest.fn(() => Promise.resolve({
1399
- id: 1,
1400
- jobTitle: '/categories/2',
1401
- }));
1402
-
1403
- mockStores.employee_profiles = {
1404
- actions: {
1405
- save,
1406
- },
1407
- getters: {
1408
- columns: [],
1409
- },
1410
- };
1411
-
1412
- await renderer.act(async () => {
1413
- renderer.create(
1414
- React.createElement(DefaultTable, {
1415
- columns: [
1416
- {
1417
- key: 'jobTitle',
1418
- label: 'Cargo',
1419
- editable: true,
1420
- list: 'categories/getItems',
1421
- formatList: item => ({
1422
- value: item.id,
1423
- label: item.name,
1424
- }),
1425
- saveFormat: value => `/categories/${parseInt(value.value || value, 10)}`,
1426
- },
1427
- ],
1428
- data: [{
1429
- id: 1,
1430
- jobTitle: '/categories/1',
1431
- }],
1432
- showColumnFiltersButton: false,
1433
- showRowActions: false,
1434
- storeName: 'employee_profiles',
1435
- }),
1436
- );
1437
- });
1438
-
1439
- expect(mockCapturedDefaultInputProps.length).toBeGreaterThan(0);
1440
-
1441
- await renderer.act(async () => {
1442
- await mockCapturedDefaultInputProps[0].onSave({
1443
- value: '2',
1444
- label: 'Departamento',
1445
- object: {id: 2, name: 'Departamento'},
1446
- });
1447
- });
1448
-
1449
- expect(save).toHaveBeenCalledWith({
1450
- id: '1',
1451
- jobTitle: '/categories/2',
1452
- [STORE_ACTION_META_KEY]: {
1453
- savedItemPatch: {
1454
- jobTitle: {
1455
- id: 2,
1456
- name: 'Departamento',
1457
- },
1458
- },
1459
- },
1460
- });
1461
- });
1462
- });