@controleonline/ui-default 1.0.276 → 1.0.278

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 +29 -29
  11. package/src/react/components/table/DefaultTableCards.js +18 -7
  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 +76 -3
@@ -0,0 +1,441 @@
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');
2
+ registerDefaultTableTests(() => {
3
+ it('can show only search in the toolbar while hiding configured actions and controls', () => {
4
+ mockState.windowDimensions = {width: 375, height: 667};
5
+ mockState.stores.categories = {
6
+ actions: {},
7
+ getters: {
8
+ columns: [{key: 'name', label: 'Nome', searchable: true}],
9
+ configs: {
10
+ import: {enabled: true, importType: 'product', label: 'Importar CSV'},
11
+ showSearch: true,
12
+ },
13
+ items: [],
14
+ totalItems: 17,
15
+ },
16
+ };
17
+ let tree;
18
+
19
+ renderer.act(() => {
20
+ tree = renderer.create(
21
+ React.createElement(DefaultTable, {
22
+ columns: [{key: 'name', label: 'Nome', searchable: true}],
23
+ data: [],
24
+ showToolbarActions: false,
25
+ showToolbarControls: false,
26
+ showTotalItemsInFooter: false,
27
+ storeName: 'categories',
28
+ }),
29
+ );
30
+ });
31
+
32
+ const iconNames = tree.root.findAllByType('icon').map(node => node.props.name);
33
+ expect(iconNames).toContain('search');
34
+ expect(iconNames).not.toEqual(
35
+ expect.arrayContaining(['upload', 'code', 'filter', 'list', 'columns']),
36
+ );
37
+ expect(tree.root.findAllByType('Text').map(node => node.props.children)).not.toContain('Importar CSV');
38
+ });
39
+ it('keeps the list/card toggle visible on compact layouts even when cards are forced', () => {
40
+ let tree;
41
+
42
+ renderer.act(() => {
43
+ tree = renderer.create(
44
+ React.createElement(DefaultTable, {
45
+ columns: [{key: 'name', label: 'Nome'}],
46
+ data: [],
47
+ showColumnFiltersButton: false,
48
+ }),
49
+ );
50
+ });
51
+
52
+ const iconNames = tree.root.findAllByType('icon').map(node => node.props.name);
53
+
54
+ expect(iconNames).toEqual(expect.arrayContaining(['list', 'columns']));
55
+ });
56
+ it('respects the compact grid/list toggle in the table body', () => {
57
+ mockState.windowDimensions = {width: 480, height: 800};
58
+ mockState.stores.orders = {
59
+ actions: {},
60
+ getters: {
61
+ columns: [{key: 'name', label: 'Nome'}],
62
+ items: [{id: 1, name: 'Pedido'}],
63
+ visibleColumns: {},
64
+ },
65
+ };
66
+ let tree;
67
+
68
+ renderer.act(() => {
69
+ tree = renderer.create(
70
+ React.createElement(DefaultTable, {
71
+ data: [{id: 1, name: 'Pedido'}],
72
+ showColumnFiltersButton: false,
73
+ storeName: 'orders',
74
+ }),
75
+ );
76
+ });
77
+
78
+ expect(
79
+ tree.root.findAllByType('ScrollView').some(node => node.props.horizontal === true),
80
+ ).toBe(false);
81
+
82
+ const toggleViewButton = tree.root
83
+ .findAllByType('TouchableOpacity')
84
+ .find(node => node.findAllByType('icon').some(icon => icon.props.name === 'list'));
85
+
86
+ renderer.act(() => {
87
+ toggleViewButton.props.onPress();
88
+ tree.update(
89
+ React.createElement(DefaultTable, {
90
+ data: [{id: 1, name: 'Pedido'}],
91
+ showColumnFiltersButton: false,
92
+ storeName: 'orders',
93
+ }),
94
+ );
95
+ });
96
+
97
+ expect(
98
+ tree.root.findAllByType('ScrollView').some(node => node.props.horizontal === true),
99
+ ).toBe(true);
100
+ expect(tree.root.findAllByType('icon').map(node => node.props.name)).toContain('grid');
101
+ });
102
+ it('pins identity and action columns on table view', () => {
103
+ mockState.windowDimensions = {width: 1200, height: 800};
104
+ mockState.stores.orders = {
105
+ actions: {},
106
+ getters: {
107
+ columns: [
108
+ {key: 'id', label: 'ID', isIdentity: true},
109
+ {key: 'name', label: 'Nome'},
110
+ ],
111
+ items: [{id: 1, name: 'Pedido'}],
112
+ visibleColumns: {},
113
+ },
114
+ };
115
+ const rowActionsComponent = () => React.createElement('row-actions');
116
+ let tree;
117
+
118
+ renderer.act(() => {
119
+ tree = renderer.create(
120
+ React.createElement(DefaultTable, {
121
+ rowActionsComponent,
122
+ storeName: 'orders',
123
+ }),
124
+ );
125
+ });
126
+
127
+ const renderedStyles = [
128
+ ...tree.root.findAllByType('View'),
129
+ ...tree.root.findAllByType('TouchableOpacity'),
130
+ ].map(node => flattenStyle(node.props.style));
131
+
132
+ expect(renderedStyles).toEqual(
133
+ expect.arrayContaining([
134
+ expect.objectContaining({position: 'sticky', left: 0}),
135
+ expect.objectContaining({position: 'sticky', right: 0}),
136
+ ]),
137
+ );
138
+
139
+ const horizontalScroll = tree.root
140
+ .findAllByType('ScrollView')
141
+ .find(node => node.props.horizontal);
142
+
143
+ renderer.act(() => {
144
+ horizontalScroll.props.onLayout({nativeEvent: {layout: {width: 300}}});
145
+ horizontalScroll.props.onContentSizeChange(600, 200);
146
+ horizontalScroll.props.onScroll({nativeEvent: {contentOffset: {x: 120}}});
147
+ });
148
+
149
+ const scrolledStyles = [
150
+ ...tree.root.findAllByType('View'),
151
+ ...tree.root.findAllByType('TouchableOpacity'),
152
+ ].map(node => flattenStyle(node.props.style));
153
+
154
+ expect(scrolledStyles).toEqual(
155
+ expect.arrayContaining([
156
+ expect.objectContaining({position: 'sticky', left: 0}),
157
+ expect.objectContaining({position: 'sticky', right: 0}),
158
+ ]),
159
+ );
160
+ });
161
+ it('renders a custom row actions component with the configured actions width', () => {
162
+ mockState.windowDimensions = {width: 1200, height: 800};
163
+ mockState.stores.orders = {
164
+ actions: {},
165
+ getters: {
166
+ columns: [
167
+ {key: 'id', label: 'ID', isIdentity: true},
168
+ {key: 'name', label: 'Nome'},
169
+ ],
170
+ items: [{id: 12, name: 'Pedido'}],
171
+ visibleColumns: {},
172
+ },
173
+ };
174
+ const rowActionsComponent = props =>
175
+ React.createElement('row-actions', props, props.row.name);
176
+ let tree;
177
+
178
+ renderer.act(() => {
179
+ tree = renderer.create(
180
+ React.createElement(DefaultTable, {
181
+ onEditRow: jest.fn(),
182
+ rowActionsComponent,
183
+ rowActionsWidth: 180,
184
+ storeName: 'orders',
185
+ }),
186
+ );
187
+ });
188
+
189
+ const rowActions = tree.root.findByType('row-actions');
190
+ expect(rowActions.props.row).toEqual({id: 12, name: 'Pedido'});
191
+ expect(rowActions.props.storeName).toBe('orders');
192
+ expect(typeof rowActions.props.openEdit).toBe('function');
193
+ expect(rowActions.props.helpers).toEqual(
194
+ expect.objectContaining({
195
+ openEdit: expect.any(Function),
196
+ }),
197
+ );
198
+
199
+ const renderedStyles = [
200
+ ...tree.root.findAllByType('View'),
201
+ ...tree.root.findAllByType('TouchableOpacity'),
202
+ ].map(node => flattenStyle(node.props.style));
203
+
204
+ expect(renderedStyles).toEqual(
205
+ expect.arrayContaining([
206
+ expect.objectContaining({
207
+ flexBasis: 180,
208
+ maxWidth: 180,
209
+ minWidth: 180,
210
+ width: 180,
211
+ }),
212
+ ]),
213
+ );
214
+ });
215
+ it('keeps only the identity column pinned when row actions are not pinned', () => {
216
+ mockState.windowDimensions = {width: 1200, height: 800};
217
+ mockState.stores.orders = {
218
+ actions: {},
219
+ getters: {
220
+ columns: [
221
+ {key: 'id', label: 'ID', isIdentity: true},
222
+ {key: 'name', label: 'Nome'},
223
+ ],
224
+ items: [{id: 1, name: 'Pedido'}],
225
+ visibleColumns: {},
226
+ },
227
+ };
228
+ const rowActionsComponent = () => React.createElement('row-actions');
229
+ let tree;
230
+
231
+ renderer.act(() => {
232
+ tree = renderer.create(
233
+ React.createElement(DefaultTable, {
234
+ pinRowActions: false,
235
+ rowActionsComponent,
236
+ storeName: 'orders',
237
+ }),
238
+ );
239
+ });
240
+
241
+ const renderedStyles = [
242
+ ...tree.root.findAllByType('View'),
243
+ ...tree.root.findAllByType('TouchableOpacity'),
244
+ ].map(node => flattenStyle(node.props.style));
245
+
246
+ expect(renderedStyles).toEqual(
247
+ expect.arrayContaining([
248
+ expect.objectContaining({position: 'sticky', left: 0}),
249
+ ]),
250
+ );
251
+ expect(renderedStyles).not.toEqual(
252
+ expect.arrayContaining([
253
+ expect.objectContaining({position: 'sticky', right: 0}),
254
+ ]),
255
+ );
256
+
257
+ const horizontalScroll = tree.root
258
+ .findAllByType('ScrollView')
259
+ .find(node => node.props.horizontal);
260
+
261
+ renderer.act(() => {
262
+ horizontalScroll.props.onLayout({nativeEvent: {layout: {width: 300}}});
263
+ horizontalScroll.props.onContentSizeChange(600, 200);
264
+ horizontalScroll.props.onScroll({nativeEvent: {contentOffset: {x: 120}}});
265
+ });
266
+
267
+ const scrolledStyles = [
268
+ ...tree.root.findAllByType('View'),
269
+ ...tree.root.findAllByType('TouchableOpacity'),
270
+ ].map(node => flattenStyle(node.props.style));
271
+
272
+ expect(scrolledStyles).toEqual(
273
+ expect.arrayContaining([
274
+ expect.objectContaining({position: 'sticky', left: 0}),
275
+ ]),
276
+ );
277
+ expect(scrolledStyles).not.toEqual(
278
+ expect.arrayContaining([
279
+ expect.objectContaining({position: 'sticky', right: 0}),
280
+ ]),
281
+ );
282
+ });
283
+ it('keeps the complete search aligned in the toolbar when there is enough room', () => {
284
+ mockState.windowDimensions = {width: 430, height: 932};
285
+ mockState.stores.orders = {
286
+ actions: {},
287
+ getters: {
288
+ columns: [{key: 'name', label: 'Nome', searchable: true}],
289
+ },
290
+ };
291
+ let tree;
292
+
293
+ renderer.act(() => {
294
+ tree = renderer.create(
295
+ React.createElement(DefaultTable, {
296
+ columns: [{key: 'name', label: 'Nome'}],
297
+ data: [],
298
+ storeName: 'orders',
299
+ }),
300
+ );
301
+ });
302
+
303
+ expect(tree.root.findAllByType('DefaultSearch')).toHaveLength(1);
304
+ expect(tree.root.findAllByType('icon').map(node => node.props.name)).not.toContain('search');
305
+ });
306
+ it('hides the toolbar when the screen disables it', () => {
307
+ mockState.windowDimensions = {width: 430, height: 932};
308
+ mockState.stores.orders = {
309
+ actions: {},
310
+ getters: {
311
+ columns: [{key: 'name', label: 'Nome', searchable: true}],
312
+ },
313
+ };
314
+ let tree;
315
+
316
+ renderer.act(() => {
317
+ tree = renderer.create(
318
+ React.createElement(DefaultTable, {
319
+ columns: [{key: 'name', label: 'Nome', searchable: true}],
320
+ data: [],
321
+ showToolbar: false,
322
+ storeName: 'orders',
323
+ }),
324
+ );
325
+ });
326
+
327
+ expect(tree.root.findAllByType('DefaultSearch')).toHaveLength(0);
328
+ expect(tree.root.findAllByType('icon').map(node => node.props.name)).not.toEqual(
329
+ expect.arrayContaining(['columns', 'grid', 'list', 'search']),
330
+ );
331
+ });
332
+ it('keeps add available as a floating button when the toolbar is hidden', () => {
333
+ const onAdd = jest.fn();
334
+ mockState.windowDimensions = {width: 430, height: 932};
335
+ mockState.stores.orders = {
336
+ actions: {},
337
+ getters: {
338
+ add: true,
339
+ columns: [{key: 'name', label: 'Nome', searchable: true}],
340
+ },
341
+ };
342
+ let tree;
343
+
344
+ renderer.act(() => {
345
+ tree = renderer.create(
346
+ React.createElement(DefaultTable, {
347
+ columns: [{key: 'name', label: 'Nome', searchable: true}],
348
+ data: [],
349
+ onAdd,
350
+ showToolbar: false,
351
+ storeName: 'orders',
352
+ }),
353
+ );
354
+ });
355
+
356
+ const addButton = tree.root
357
+ .findAllByType('TouchableOpacity')
358
+ .find(node => node.findAllByType('icon').some(icon => icon.props.name === 'plus'));
359
+
360
+ expect(addButton).toBeTruthy();
361
+ expect(addButton.props.accessibilityRole).toBe('button');
362
+ expect(addButton.props.accessibilityLabel).toBe('Adicionar');
363
+
364
+ renderer.act(() => addButton.props.onPress());
365
+
366
+ expect(onAdd).toHaveBeenCalledTimes(1);
367
+ });
368
+ it('opens the default create form when add has no custom handler', () => {
369
+ mockState.windowDimensions = {width: 430, height: 932};
370
+ const save = jest.fn(() => Promise.resolve({id: 10}));
371
+ mockState.stores.invoice = {
372
+ actions: {save},
373
+ getters: {
374
+ add: true,
375
+ columns: [{key: 'payer', label: 'Pagador', editable: true}],
376
+ },
377
+ };
378
+ let tree;
379
+
380
+ renderer.act(() => {
381
+ tree = renderer.create(
382
+ React.createElement(DefaultTable, {
383
+ requestParams: {receiver: 21},
384
+ showToolbar: false,
385
+ storeName: 'invoice',
386
+ }),
387
+ );
388
+ });
389
+
390
+ const addButton = tree.root
391
+ .findAllByType('TouchableOpacity')
392
+ .find(node => node.findAllByType('icon').some(icon => icon.props.name === 'plus'));
393
+
394
+ expect(tree.root.findAllByType('Modal').some(node => node.props.visible)).toBe(false);
395
+
396
+ renderer.act(() => addButton.props.onPress());
397
+
398
+ expect(tree.root.findAllByType('Modal').some(node => node.props.visible)).toBe(true);
399
+ expect(mockState.capturedDefaultFormProps[mockState.capturedDefaultFormProps.length - 1]).toEqual(
400
+ expect.objectContaining({
401
+ actions: expect.objectContaining({save}),
402
+ mode: 'create',
403
+ row: {receiver: 21},
404
+ storeName: 'invoice',
405
+ }),
406
+ );
407
+ });
408
+ it('collapses search into an icon on narrow toolbars and opens the search modal', () => {
409
+ mockState.windowDimensions = {width: 375, height: 667};
410
+ mockState.stores.orders = {
411
+ actions: {},
412
+ getters: {
413
+ columns: [{key: 'name', label: 'Nome', searchable: true}],
414
+ },
415
+ };
416
+ let tree;
417
+
418
+ renderer.act(() => {
419
+ tree = renderer.create(
420
+ React.createElement(DefaultTable, {
421
+ columns: [{key: 'name', label: 'Nome'}],
422
+ data: [],
423
+ storeName: 'orders',
424
+ }),
425
+ );
426
+ });
427
+
428
+ expect(tree.root.findAllByType('DefaultSearch')).toHaveLength(0);
429
+ const searchButton = tree.root
430
+ .findAllByType('TouchableOpacity')
431
+ .find(node => node.findAllByType('icon').some(icon => icon.props.name === 'search'));
432
+
433
+ expect(searchButton.props.accessibilityRole).toBe('button');
434
+ expect(searchButton.props.accessibilityLabel).toBe('Buscar');
435
+
436
+ renderer.act(() => searchButton.props.onPress());
437
+
438
+ expect(tree.root.findAllByType('DefaultSearch')).toHaveLength(1);
439
+ expect(tree.root.findByType('DefaultSearch').props.autoFocus).toBe(true);
440
+ });
441
+ });