@controleonline/ui-default 1.0.270 → 1.0.273
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/.github/workflows/integration-source-gate.yml +68 -0
- package/.github/workflows/manager-worker.yml +19 -0
- package/.github/workflows/publish-npm.yml +91 -0
- package/AGENTS.md +46 -50
- package/docs/technical/Contrato-de-colunas.md +67 -0
- package/docs/technical/Default-Components.md +76 -0
- package/docs/technical/DefaultForm.md +80 -0
- package/docs/technical/DefaultInput-e-DefaultSelect.md +73 -0
- package/docs/technical/DefaultTable.md +272 -0
- package/docs/wiki.md +1 -0
- package/package.json +4 -3
- package/scripts/validate-wiki-pointer.cjs +13 -0
- package/src/react/components/address/DefaultAddress.js +446 -0
- package/src/react/components/address/DefaultAddress.styles.js +133 -0
- package/src/react/components/address/DefaultAddressCoordFields.js +59 -0
- package/src/react/components/address/DefaultAddressMapPane.js +55 -0
- package/src/react/components/address/LatLonReadonlyFields.js +51 -0
- package/src/react/components/address/defaultAddressHelpers.js +163 -0
- package/src/react/components/address/defaultAddressStyles.js +148 -0
- package/src/react/components/address/defaultAddressUtils.js +129 -0
- package/src/react/components/extra-data/DefaultExtraData.js +152 -0
- package/src/react/components/extra-data/DefaultExtraData.styles.js +61 -0
- package/src/react/components/files/DefaultFile.js +231 -46
- package/src/react/components/files/DefaultFileColumn.js +123 -0
- package/src/react/components/files/DefaultFileColumn.styles.js +52 -0
- package/src/react/components/files/DefaultFileColumn.utils.js +104 -0
- package/src/react/components/filters/CompactFilterSelector.js +51 -35
- package/src/react/components/filters/DefaultColumnFilter.js +60 -48
- package/src/react/components/filters/DefaultExternalFilters.js +324 -265
- package/src/react/components/filters/filterValue.js +65 -0
- package/src/react/components/form/DefaultForm.js +99 -79
- package/src/react/components/help/DefaultTooltip.js +48 -12
- package/src/react/components/help/DefaultTooltip.styles.js +20 -0
- package/src/react/components/inputs/DefaultColorInput.js +153 -0
- package/src/react/components/inputs/DefaultFeatherIconPicker.js +146 -0
- package/src/react/components/inputs/DefaultFeatherIconPicker.styles.js +113 -0
- package/src/react/components/inputs/DefaultInput.js +296 -141
- package/src/react/components/inputs/DefaultInput.styles.js +26 -0
- package/src/react/components/inputs/DefaultSelect.js +134 -123
- package/src/react/components/inputs/defaultInputUtils.js +262 -213
- package/src/react/components/map/DefaultMap.native.js +73 -73
- package/src/react/components/map/DefaultMap.shared.js +911 -762
- package/src/react/components/map/DefaultMap.web.js +124 -68
- package/src/react/components/table/DefaultColumnMenu.js +22 -3
- package/src/react/components/table/DefaultDebug.js +44 -37
- package/src/react/components/table/DefaultSearchModal.js +9 -1
- package/src/react/components/table/DefaultTable.grouping.js +98 -0
- package/src/react/components/table/DefaultTable.js +217 -111
- package/src/react/components/table/DefaultTable.styles.js +369 -310
- package/src/react/components/table/DefaultTable.utils.js +38 -4
- package/src/react/components/table/DefaultTableBody.js +13 -8
- package/src/react/components/table/DefaultTableCards.js +20 -1
- package/src/react/components/table/DefaultTableControls.js +180 -205
- package/src/react/components/table/DefaultTableImportModal.js +108 -0
- package/src/react/components/table/DefaultTableInput.js +1 -0
- package/src/react/components/table/DefaultTableRowActions.js +1 -1
- package/src/react/components/table/DefaultTableRows.js +357 -325
- package/src/react/components/table/DefaultTableSearch.js +28 -5
- package/src/react/components/table/DefaultTableToolbar.js +92 -3
- package/src/react/components/table/useDefaultTablePagination.js +56 -7
- package/src/react/components/table/useDefaultTableStoreSync.js +164 -0
- package/src/react/components/upload/DefaultUpload.js +406 -0
- package/src/react/components/upload/DefaultUpload.styles.js +153 -0
- package/src/react/components/upload/DefaultUploadAttachmentsList.js +146 -0
- package/src/react/components/upload/DefaultUploadManagerModal.js +247 -0
- package/src/react/components/upload/defaultUploadHelpers.js +108 -0
- package/src/react/components/upload/defaultUploadLibrary.js +248 -0
- package/src/react/components/upload/fileUpload.js +93 -0
- package/src/react/hooks/usePostalCodeLookup.js +159 -0
- package/src/react/index.js +6 -0
- package/src/react/services/addressFormUtils.js +262 -0
- package/src/react/services/addressGeo.js +230 -0
- package/src/react/utils/tableVisibleColumnsPreferences.js +113 -15
- package/src/tests/react/components/address/defaultAddressHelpers.test.js +104 -0
- package/src/tests/react/components/files/DefaultFile.test.js +22 -6
- package/src/tests/react/components/files/DefaultFileColumn.company.test.js +24 -0
- package/src/tests/react/components/form/DefaultForm.test.js +123 -0
- package/src/tests/react/components/help/DefaultTooltip.test.js +93 -0
- package/src/tests/react/components/inputs/DefaultSelect.test.js +108 -79
- package/src/tests/react/components/table/DefaultTable.grouping.test.js +49 -0
- package/src/tests/react/components/table/DefaultTable.test.js +53 -2
- package/src/tests/react/components/table/DefaultTable.utils.test.js +10 -0
- package/src/tests/react/components/table/DefaultTableControls.filters.test.mjs +21 -0
- package/src/tests/react/components/table/DefaultTableRowActions.test.js +23 -0
- package/src/tests/react/components/table/DefaultTableRows.rowActions.test.js +152 -0
- package/src/tests/react/components/table/resolveRowBackgroundColor.test.mjs +45 -0
- package/src/tests/react/components/table/useDefaultTableStoreSync.test.js +161 -0
- package/src/tests/react/components/upload/DefaultUpload.test.js +353 -0
- package/src/tests/react/components/upload/defaultUploadLibrary.test.js +104 -0
- package/src/tests/react/components/upload/peopleMediaLibraryPreview.test.mjs +57 -0
- package/src/tests/react/filters/filterValue.test.js +46 -0
- package/src/tests/react/hooks/usePostalCodeLookup.test.js +107 -0
- package/src/tests/react/inputs/defaultInputUtils.test.js +9 -1
- package/src/tests/react/services/SMOKE-637-cep-hydrate.md +45 -0
- package/src/tests/react/services/addressFormUtils.test.js +179 -0
- package/src/tests/react/services/addressGeoLookup.async.test.js +104 -0
- package/src/tests/react/services/addressGeoLookup.test.js +105 -0
- package/src/tests/react/services/smoke-637/form-cep-12941040.png +0 -0
- package/src/tests/react/services/smoke-637/index.html +132 -0
- package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +96 -49
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const React = require('react');
|
|
2
|
-
const renderer = require('react-test-renderer');
|
|
1
|
+
const React = require('react');
|
|
2
|
+
const renderer = require('react-test-renderer');
|
|
3
3
|
const {jest} = require('@jest/globals');
|
|
4
4
|
|
|
5
5
|
const {beforeEach, describe, expect, it} = global;
|
|
@@ -14,37 +14,37 @@ jest.mock('@store', () => ({
|
|
|
14
14
|
}));
|
|
15
15
|
|
|
16
16
|
jest.mock('react-native', () => {
|
|
17
|
-
const React = require('react');
|
|
18
|
-
const createComponent = name => props =>
|
|
19
|
-
React.createElement(name, props, props.children);
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
Modal: createComponent('Modal'),
|
|
23
|
-
ScrollView: createComponent('ScrollView'),
|
|
24
|
-
StyleSheet: {create: value => value},
|
|
25
|
-
Text: createComponent('Text'),
|
|
26
|
-
TextInput: createComponent('TextInput'),
|
|
27
|
-
TouchableOpacity: createComponent('TouchableOpacity'),
|
|
28
|
-
View: createComponent('View'),
|
|
29
|
-
};
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
jest.mock('react-native-vector-icons/Feather', () => {
|
|
33
|
-
const React = require('react');
|
|
34
|
-
return props => React.createElement('Icon', props, props.children);
|
|
35
|
-
});
|
|
36
|
-
|
|
17
|
+
const React = require('react');
|
|
18
|
+
const createComponent = name => props =>
|
|
19
|
+
React.createElement(name, props, props.children);
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
Modal: createComponent('Modal'),
|
|
23
|
+
ScrollView: createComponent('ScrollView'),
|
|
24
|
+
StyleSheet: {create: value => value},
|
|
25
|
+
Text: createComponent('Text'),
|
|
26
|
+
TextInput: createComponent('TextInput'),
|
|
27
|
+
TouchableOpacity: createComponent('TouchableOpacity'),
|
|
28
|
+
View: createComponent('View'),
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
jest.mock('react-native-vector-icons/Feather', () => {
|
|
33
|
+
const React = require('react');
|
|
34
|
+
return props => React.createElement('Icon', props, props.children);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
37
|
const DefaultSelect = require('../../../../react/components/inputs/DefaultSelect').default;
|
|
38
|
-
|
|
39
|
-
const createDeferred = () => {
|
|
40
|
-
let resolve;
|
|
41
|
-
const promise = new Promise(nextResolve => {
|
|
42
|
-
resolve = nextResolve;
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
return {promise, resolve};
|
|
46
|
-
};
|
|
47
|
-
|
|
38
|
+
|
|
39
|
+
const createDeferred = () => {
|
|
40
|
+
let resolve;
|
|
41
|
+
const promise = new Promise(nextResolve => {
|
|
42
|
+
resolve = nextResolve;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return {promise, resolve};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
48
|
describe('DefaultSelect', () => {
|
|
49
49
|
beforeEach(() => {
|
|
50
50
|
mockStores = {
|
|
@@ -64,53 +64,53 @@ describe('DefaultSelect', () => {
|
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
it('waits for onBeforeOpen before showing the options modal', async () => {
|
|
67
|
-
const gate = createDeferred();
|
|
68
|
-
const onBeforeOpen = jest.fn(() => gate.promise);
|
|
69
|
-
const onStartEditing = jest.fn();
|
|
70
|
-
let tree;
|
|
71
|
-
let openPromise;
|
|
72
|
-
|
|
73
|
-
await renderer.act(async () => {
|
|
74
|
-
tree = renderer.create(
|
|
75
|
-
React.createElement(DefaultSelect, {
|
|
76
|
-
column: {
|
|
77
|
-
key: 'jobTitle',
|
|
78
|
-
label: 'Cargo',
|
|
79
|
-
list: 'categories/getItems',
|
|
80
|
-
},
|
|
81
|
-
editing: false,
|
|
82
|
-
getOptionsForColumn: () => [
|
|
83
|
-
{
|
|
84
|
-
key: '1',
|
|
85
|
-
label: 'Analista de RH',
|
|
86
|
-
},
|
|
87
|
-
],
|
|
88
|
-
onBeforeOpen,
|
|
89
|
-
onStartEditing,
|
|
90
|
-
row: {},
|
|
91
|
-
storeName: 'employee_profiles',
|
|
92
|
-
value: '1',
|
|
93
|
-
}),
|
|
94
|
-
);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const readButton = tree.root.findAllByType('TouchableOpacity')[0];
|
|
98
|
-
const modal = tree.root.findByType('Modal');
|
|
99
|
-
|
|
100
|
-
expect(modal.props.visible).toBe(false);
|
|
101
|
-
|
|
102
|
-
renderer.act(() => {
|
|
103
|
-
openPromise = readButton.props.onPress();
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
expect(onStartEditing).toHaveBeenCalledTimes(1);
|
|
107
|
-
expect(onBeforeOpen).toHaveBeenCalledTimes(1);
|
|
108
|
-
expect(modal.props.visible).toBe(false);
|
|
109
|
-
|
|
110
|
-
await renderer.act(async () => {
|
|
111
|
-
gate.resolve();
|
|
112
|
-
await openPromise;
|
|
113
|
-
});
|
|
67
|
+
const gate = createDeferred();
|
|
68
|
+
const onBeforeOpen = jest.fn(() => gate.promise);
|
|
69
|
+
const onStartEditing = jest.fn();
|
|
70
|
+
let tree;
|
|
71
|
+
let openPromise;
|
|
72
|
+
|
|
73
|
+
await renderer.act(async () => {
|
|
74
|
+
tree = renderer.create(
|
|
75
|
+
React.createElement(DefaultSelect, {
|
|
76
|
+
column: {
|
|
77
|
+
key: 'jobTitle',
|
|
78
|
+
label: 'Cargo',
|
|
79
|
+
list: 'categories/getItems',
|
|
80
|
+
},
|
|
81
|
+
editing: false,
|
|
82
|
+
getOptionsForColumn: () => [
|
|
83
|
+
{
|
|
84
|
+
key: '1',
|
|
85
|
+
label: 'Analista de RH',
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
onBeforeOpen,
|
|
89
|
+
onStartEditing,
|
|
90
|
+
row: {},
|
|
91
|
+
storeName: 'employee_profiles',
|
|
92
|
+
value: '1',
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const readButton = tree.root.findAllByType('TouchableOpacity')[0];
|
|
98
|
+
const modal = tree.root.findByType('Modal');
|
|
99
|
+
|
|
100
|
+
expect(modal.props.visible).toBe(false);
|
|
101
|
+
|
|
102
|
+
renderer.act(() => {
|
|
103
|
+
openPromise = readButton.props.onPress();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(onStartEditing).toHaveBeenCalledTimes(1);
|
|
107
|
+
expect(onBeforeOpen).toHaveBeenCalledTimes(1);
|
|
108
|
+
expect(modal.props.visible).toBe(false);
|
|
109
|
+
|
|
110
|
+
await renderer.act(async () => {
|
|
111
|
+
gate.resolve();
|
|
112
|
+
await openPromise;
|
|
113
|
+
});
|
|
114
114
|
|
|
115
115
|
expect(modal.props.visible).toBe(true);
|
|
116
116
|
});
|
|
@@ -254,4 +254,33 @@ describe('DefaultSelect', () => {
|
|
|
254
254
|
]),
|
|
255
255
|
);
|
|
256
256
|
});
|
|
257
|
+
|
|
258
|
+
it('uses the resolved form label as the modal search placeholder', async () => {
|
|
259
|
+
let tree;
|
|
260
|
+
|
|
261
|
+
await renderer.act(async () => {
|
|
262
|
+
tree = renderer.create(
|
|
263
|
+
React.createElement(DefaultSelect, {
|
|
264
|
+
column: {
|
|
265
|
+
key: 'paymentType',
|
|
266
|
+
label: 'Payment Type.payment Type',
|
|
267
|
+
list: 'walletPaymentType/getItems',
|
|
268
|
+
searchParam: 'paymentType.paymentType',
|
|
269
|
+
},
|
|
270
|
+
editing: true,
|
|
271
|
+
label: 'Tipo de pagamento',
|
|
272
|
+
row: {},
|
|
273
|
+
storeName: 'invoice',
|
|
274
|
+
variant: 'form',
|
|
275
|
+
}),
|
|
276
|
+
);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
await renderer.act(async () => {
|
|
280
|
+
await tree.root.findAllByType('TouchableOpacity')[0].props.onPress();
|
|
281
|
+
await Promise.resolve();
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
expect(tree.root.findByType('TextInput').props.placeholder).toBe('Tipo de pagamento');
|
|
285
|
+
});
|
|
257
286
|
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
flattenGroupedTableItems,
|
|
3
|
+
getGroupingColumns,
|
|
4
|
+
groupTableRows,
|
|
5
|
+
toggleGroupSelection,
|
|
6
|
+
toggleSelectedId,
|
|
7
|
+
} from '../../../../src/react/components/table/DefaultTable.grouping';
|
|
8
|
+
|
|
9
|
+
const columns = [
|
|
10
|
+
{ name: 'companyName', grouping: true },
|
|
11
|
+
{ name: 'addressLabel', grouping: true },
|
|
12
|
+
{ name: 'invoiceNumber' },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const rows = [
|
|
16
|
+
{ id: 1, companyName: 'A', addressLabel: 'Rua 1', invoiceNumber: 10 },
|
|
17
|
+
{ id: 2, companyName: 'A', addressLabel: 'Rua 1', invoiceNumber: 11 },
|
|
18
|
+
{ id: 3, companyName: 'B', addressLabel: 'Rua 2', invoiceNumber: 12 },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
describe('DefaultTable grouping', () => {
|
|
22
|
+
it('reads boolean grouping from store columns', () => {
|
|
23
|
+
expect(getGroupingColumns(columns).map(column => column.name)).toEqual([
|
|
24
|
+
'companyName',
|
|
25
|
+
'addressLabel',
|
|
26
|
+
]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('groups rows by grouping columns', () => {
|
|
30
|
+
const groups = groupTableRows(rows, columns);
|
|
31
|
+
expect(groups).toHaveLength(2);
|
|
32
|
+
expect(groups[0].count).toBe(2);
|
|
33
|
+
expect(groups[1].count).toBe(1);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('flattens group headers and rows', () => {
|
|
37
|
+
const items = flattenGroupedTableItems(rows, columns);
|
|
38
|
+
expect(items[0].type).toBe('group');
|
|
39
|
+
expect(items.filter(item => item.type === 'row')).toHaveLength(3);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('toggles row and group selection', () => {
|
|
43
|
+
const one = toggleSelectedId([], rows[0]);
|
|
44
|
+
expect(one).toEqual(['1']);
|
|
45
|
+
const group = toggleGroupSelection(one, [rows[0], rows[1]]);
|
|
46
|
+
expect(group).toEqual(['1', '2']);
|
|
47
|
+
expect(toggleGroupSelection(group, [rows[0], rows[1]])).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -9,6 +9,7 @@ let consoleErrorSpy = null;
|
|
|
9
9
|
let mockStores = {};
|
|
10
10
|
let mockWindowDimensions = {width: 480, height: 800};
|
|
11
11
|
let mockCapturedColumnFilterProps = [];
|
|
12
|
+
let mockCapturedDefaultFormProps = [];
|
|
12
13
|
let mockCapturedDefaultInputProps = [];
|
|
13
14
|
|
|
14
15
|
const createLocalStorageMock = () => {
|
|
@@ -28,6 +29,8 @@ const createLocalStorageMock = () => {
|
|
|
28
29
|
};
|
|
29
30
|
};
|
|
30
31
|
|
|
32
|
+
global.localStorage = createLocalStorageMock();
|
|
33
|
+
|
|
31
34
|
jest.mock('@store', () => ({
|
|
32
35
|
getAllStores: jest.fn(() => ({})),
|
|
33
36
|
useStore: jest.fn(name => mockStores[name] || {actions: {}, getters: {}}),
|
|
@@ -98,9 +101,15 @@ jest.mock('../../../../react/components/filters/DefaultSearch', () => props => {
|
|
|
98
101
|
return React.createElement('DefaultSearch', props);
|
|
99
102
|
});
|
|
100
103
|
|
|
101
|
-
jest.mock('../../../../react/components/
|
|
104
|
+
jest.mock('../../../../react/components/table/DefaultTableImportModal', () => props => {
|
|
102
105
|
const React = require('react');
|
|
103
|
-
return React.createElement('
|
|
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);
|
|
104
113
|
});
|
|
105
114
|
|
|
106
115
|
jest.mock('../../../../react/components/inputs/DefaultInput', () => props => {
|
|
@@ -230,6 +239,7 @@ describe('DefaultTable', () => {
|
|
|
230
239
|
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
231
240
|
mockWindowDimensions = {width: 480, height: 800};
|
|
232
241
|
mockCapturedColumnFilterProps = [];
|
|
242
|
+
mockCapturedDefaultFormProps = [];
|
|
233
243
|
mockCapturedDefaultInputProps = [];
|
|
234
244
|
getAllStores.mockImplementation(() => ({}));
|
|
235
245
|
global.localStorage = createLocalStorageMock();
|
|
@@ -614,6 +624,47 @@ describe('DefaultTable', () => {
|
|
|
614
624
|
expect(onAdd).toHaveBeenCalledTimes(1);
|
|
615
625
|
});
|
|
616
626
|
|
|
627
|
+
it('opens the default create form when add has no custom handler', () => {
|
|
628
|
+
mockWindowDimensions = {width: 430, height: 932};
|
|
629
|
+
const save = jest.fn(() => Promise.resolve({id: 10}));
|
|
630
|
+
mockStores.invoice = {
|
|
631
|
+
actions: {save},
|
|
632
|
+
getters: {
|
|
633
|
+
add: true,
|
|
634
|
+
columns: [{key: 'payer', label: 'Pagador', editable: true}],
|
|
635
|
+
},
|
|
636
|
+
};
|
|
637
|
+
let tree;
|
|
638
|
+
|
|
639
|
+
renderer.act(() => {
|
|
640
|
+
tree = renderer.create(
|
|
641
|
+
React.createElement(DefaultTable, {
|
|
642
|
+
requestParams: {receiver: 21},
|
|
643
|
+
showToolbar: false,
|
|
644
|
+
storeName: 'invoice',
|
|
645
|
+
}),
|
|
646
|
+
);
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
const addButton = tree.root
|
|
650
|
+
.findAllByType('TouchableOpacity')
|
|
651
|
+
.find(node => node.findAllByType('icon').some(icon => icon.props.name === 'plus'));
|
|
652
|
+
|
|
653
|
+
expect(tree.root.findAllByType('Modal').some(node => node.props.visible)).toBe(false);
|
|
654
|
+
|
|
655
|
+
renderer.act(() => addButton.props.onPress());
|
|
656
|
+
|
|
657
|
+
expect(tree.root.findAllByType('Modal').some(node => node.props.visible)).toBe(true);
|
|
658
|
+
expect(mockCapturedDefaultFormProps[mockCapturedDefaultFormProps.length - 1]).toEqual(
|
|
659
|
+
expect.objectContaining({
|
|
660
|
+
actions: expect.objectContaining({save}),
|
|
661
|
+
mode: 'create',
|
|
662
|
+
row: {receiver: 21},
|
|
663
|
+
storeName: 'invoice',
|
|
664
|
+
}),
|
|
665
|
+
);
|
|
666
|
+
});
|
|
667
|
+
|
|
617
668
|
it('collapses search into an icon on narrow toolbars and opens the search modal', () => {
|
|
618
669
|
mockWindowDimensions = {width: 375, height: 667};
|
|
619
670
|
mockStores.orders = {
|
|
@@ -21,6 +21,7 @@ jest.mock('@controleonline/ui-common/src/react/utils/dateRangeFilter', () => ({
|
|
|
21
21
|
const {
|
|
22
22
|
getColumnMinWidth,
|
|
23
23
|
getColumnStyle,
|
|
24
|
+
parseSortNumber,
|
|
24
25
|
} = require('../../../../react/components/table/DefaultTable.utils');
|
|
25
26
|
|
|
26
27
|
describe('DefaultTable utils', () => {
|
|
@@ -34,4 +35,13 @@ describe('DefaultTable utils', () => {
|
|
|
34
35
|
]),
|
|
35
36
|
);
|
|
36
37
|
});
|
|
38
|
+
|
|
39
|
+
it('parses Brazilian thousand and decimal masks as numbers', () => {
|
|
40
|
+
expect(parseSortNumber('1.234,56')).toBeCloseTo(1234.56);
|
|
41
|
+
expect(parseSortNumber('10')).toBe(10);
|
|
42
|
+
expect(parseSortNumber('2')).toBe(2);
|
|
43
|
+
expect(parseSortNumber('1.234')).toBe(1234);
|
|
44
|
+
expect(parseSortNumber('12/03/2024')).toBeNaN();
|
|
45
|
+
expect(parseSortNumber('abc')).toBeNaN();
|
|
46
|
+
});
|
|
37
47
|
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// fluxo: outros | etapa: order-history-page-filtro | https://github.com/ControleOnline/app-community/wiki/Smoke-Test-Flows
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import assert from 'node:assert/strict';
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), '../../../../../');
|
|
9
|
+
const source = readFileSync(
|
|
10
|
+
join(root, 'src/react/components/table/DefaultTableControls.js'),
|
|
11
|
+
'utf8',
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
test('filter button opens DefaultFiltersModal in table and cards modes (#811)', () => {
|
|
15
|
+
assert.match(source, /DefaultFiltersModal/);
|
|
16
|
+
assert.match(source, /hasFilterableColumns \? \(/);
|
|
17
|
+
assert.match(source, /testID="default-table-filter-button"/);
|
|
18
|
+
// must not gate the modal only on cards / must not toggle orphan flag
|
|
19
|
+
assert.doesNotMatch(source, /tableFiltersVisible:\s*!tableFiltersVisible/);
|
|
20
|
+
assert.doesNotMatch(source, /effectiveViewMode === 'cards' \? \([\s\S]*DefaultFiltersModal/);
|
|
21
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const React = require('react');
|
|
2
|
+
const {describe, expect, it} = require('@jest/globals');
|
|
3
|
+
|
|
4
|
+
const {
|
|
5
|
+
hasDefaultTableRowActionsComponent,
|
|
6
|
+
resolveDefaultTableRowActionsWidth,
|
|
7
|
+
} = require('../../../../react/components/table/DefaultTableRowActions');
|
|
8
|
+
|
|
9
|
+
describe('DefaultTableRowActions helpers', () => {
|
|
10
|
+
it('accepts function and element types as custom row actions', () => {
|
|
11
|
+
expect(hasDefaultTableRowActionsComponent(() => null)).toBe(true);
|
|
12
|
+
expect(hasDefaultTableRowActionsComponent(React.createElement('span'))).toBe(true);
|
|
13
|
+
expect(hasDefaultTableRowActionsComponent(null)).toBe(false);
|
|
14
|
+
expect(hasDefaultTableRowActionsComponent('pdf')).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('uses configured width and falls back to 140', () => {
|
|
18
|
+
expect(resolveDefaultTableRowActionsWidth({rowActionsWidth: 168})).toBe(168);
|
|
19
|
+
expect(resolveDefaultTableRowActionsWidth({actionsColumnWidth: 96})).toBe(96);
|
|
20
|
+
expect(resolveDefaultTableRowActionsWidth({rowActionsWidth: 0})).toBe(140);
|
|
21
|
+
expect(resolveDefaultTableRowActionsWidth({})).toBe(140);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
const React = require('react');
|
|
2
|
+
const renderer = require('react-test-renderer');
|
|
3
|
+
const {describe, expect, it, beforeEach} = require('@jest/globals');
|
|
4
|
+
|
|
5
|
+
global.IS_REACT_ACT_ENVIRONMENT = true;
|
|
6
|
+
|
|
7
|
+
let mockStores = {};
|
|
8
|
+
|
|
9
|
+
jest.mock('@store', () => ({
|
|
10
|
+
useStore: jest.fn(name => mockStores[name] || {actions: {}, getters: {}}),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
jest.mock('@controleonline/ui-common/src/react/utils/storeColumns', () => ({
|
|
14
|
+
formatStoreColumnLabel: ({fallbackLabel, fieldName}) => fallbackLabel || fieldName,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
jest.mock('react-native-vector-icons/Feather', () => {
|
|
18
|
+
const React = require('react');
|
|
19
|
+
return props => React.createElement('icon', props);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
jest.mock('react-native', () => {
|
|
23
|
+
const React = require('react');
|
|
24
|
+
const createComponent = name => props =>
|
|
25
|
+
React.createElement(name, props, props.children);
|
|
26
|
+
const FlatList = props => {
|
|
27
|
+
const items = Array.isArray(props.data) ? props.data : [];
|
|
28
|
+
return React.createElement(
|
|
29
|
+
'FlatList',
|
|
30
|
+
props,
|
|
31
|
+
items.map((item, index) =>
|
|
32
|
+
props.renderItem ? props.renderItem({item, index}) : null,
|
|
33
|
+
),
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
FlatList,
|
|
38
|
+
ScrollView: createComponent('ScrollView'),
|
|
39
|
+
Text: createComponent('Text'),
|
|
40
|
+
TouchableOpacity: createComponent('TouchableOpacity'),
|
|
41
|
+
View: createComponent('View'),
|
|
42
|
+
StyleSheet: {create: value => value},
|
|
43
|
+
Platform: {select: value => value.web || value.default},
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
jest.mock('../../../../react/components/filters/DefaultColumnFilter', () => () => null);
|
|
48
|
+
jest.mock('../../../../react/components/table/DefaultTable.styles', () => ({
|
|
49
|
+
cell: {},
|
|
50
|
+
actionsCell: {},
|
|
51
|
+
pinnedActionsCell: {},
|
|
52
|
+
stickyActionsCell: {},
|
|
53
|
+
stickyHeaderCell: {},
|
|
54
|
+
stickyIdentityCell: {},
|
|
55
|
+
rowActionsGroup: {},
|
|
56
|
+
headerRow: {},
|
|
57
|
+
headerText: {},
|
|
58
|
+
sortableHeader: {},
|
|
59
|
+
row: {},
|
|
60
|
+
scroll: {},
|
|
61
|
+
content: {},
|
|
62
|
+
tableList: {},
|
|
63
|
+
tableListContent: {},
|
|
64
|
+
filterRow: {},
|
|
65
|
+
filterCell: {},
|
|
66
|
+
iconButton: {},
|
|
67
|
+
horizontalScrollContent: {},
|
|
68
|
+
}));
|
|
69
|
+
jest.mock('../../../../react/components/table/DefaultTableEmptyState', () => () => null);
|
|
70
|
+
jest.mock('../../../../react/components/table/DefaultTableInput', () => {
|
|
71
|
+
const React = require('react');
|
|
72
|
+
return ({column, row}) =>
|
|
73
|
+
React.createElement('cell', {
|
|
74
|
+
field: column?.key || column?.name,
|
|
75
|
+
value: row?.[column?.key || column?.name],
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
jest.mock('../../../../react/components/table/useDefaultTableTheme', () => () => ({
|
|
79
|
+
palette: {background: '#fff', text: '#111', textSecondary: '#666'},
|
|
80
|
+
resolvedAccentColor: '#eee',
|
|
81
|
+
tableBorderColors: {rowBorderColor: '#ddd', headerBorderColor: '#ccc'},
|
|
82
|
+
themeTokens: {},
|
|
83
|
+
}));
|
|
84
|
+
|
|
85
|
+
const DefaultTableRows = require('../../../../react/components/table/DefaultTableRows').default;
|
|
86
|
+
|
|
87
|
+
const collectTestIds = tree =>
|
|
88
|
+
[
|
|
89
|
+
...tree.root.findAllByType('View'),
|
|
90
|
+
...tree.root.findAllByType('TouchableOpacity'),
|
|
91
|
+
...tree.root.findAllByType('Text'),
|
|
92
|
+
]
|
|
93
|
+
.map(node => node.props.testID)
|
|
94
|
+
.filter(Boolean);
|
|
95
|
+
|
|
96
|
+
describe('DefaultTableRows row actions column', () => {
|
|
97
|
+
beforeEach(() => {
|
|
98
|
+
mockStores = {};
|
|
99
|
+
global.t = {t: (_store, _kind, key) => ({actions: 'Ações', edit: 'Editar'}[key])};
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('renders header + cell when rowActionsComponent is set', () => {
|
|
103
|
+
mockStores.orders = {
|
|
104
|
+
getters: {
|
|
105
|
+
columns: [{key: 'name', label: 'Nome'}],
|
|
106
|
+
items: [{id: 7, name: 'CT-e'}],
|
|
107
|
+
visibleColumns: {},
|
|
108
|
+
configs: {
|
|
109
|
+
rowActionsComponent: () => React.createElement('row-actions'),
|
|
110
|
+
showRowActions: true,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
let tree;
|
|
116
|
+
renderer.act(() => {
|
|
117
|
+
tree = renderer.create(React.createElement(DefaultTableRows, {storeName: 'orders'}));
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
expect(collectTestIds(tree)).toEqual(
|
|
121
|
+
expect.arrayContaining([
|
|
122
|
+
'default-table-row-actions-header',
|
|
123
|
+
'default-table-row-actions-7',
|
|
124
|
+
]),
|
|
125
|
+
);
|
|
126
|
+
expect(tree.root.findAllByType('row-actions')).toHaveLength(1);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('does not render an actions column when showRowActions is false', () => {
|
|
130
|
+
mockStores.orders = {
|
|
131
|
+
getters: {
|
|
132
|
+
columns: [{key: 'name', label: 'Nome'}],
|
|
133
|
+
items: [{id: 7, name: 'CT-e'}],
|
|
134
|
+
visibleColumns: {},
|
|
135
|
+
configs: {
|
|
136
|
+
rowActionsComponent: () => React.createElement('row-actions'),
|
|
137
|
+
showRowActions: false,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
let tree;
|
|
143
|
+
renderer.act(() => {
|
|
144
|
+
tree = renderer.create(React.createElement(DefaultTableRows, {storeName: 'orders'}));
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
expect(collectTestIds(tree)).not.toEqual(
|
|
148
|
+
expect.arrayContaining(['default-table-row-actions-header']),
|
|
149
|
+
);
|
|
150
|
+
expect(tree.root.findAllByType('row-actions')).toHaveLength(0);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), '../../../../../');
|
|
8
|
+
const sourcePath = join(root, 'src/react/components/table/DefaultTableRows.js');
|
|
9
|
+
const source = readFileSync(sourcePath, 'utf8');
|
|
10
|
+
|
|
11
|
+
test('resolveRowBackgroundColor ignores status.color in source', () => {
|
|
12
|
+
assert.doesNotMatch(source, /row\?\.status\?\.color/);
|
|
13
|
+
assert.doesNotMatch(source, /withAlpha\(statusColor/);
|
|
14
|
+
assert.match(source, /listItemEvenRow/);
|
|
15
|
+
assert.match(source, /listItemOddRow/);
|
|
16
|
+
assert.match(source, /export const resolveRowBackgroundColor/);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const resolveRowBackgroundColor = ({
|
|
20
|
+
selected,
|
|
21
|
+
index,
|
|
22
|
+
tableOddColor,
|
|
23
|
+
tableEvenColor,
|
|
24
|
+
selectedBackground = '#ECFEFF',
|
|
25
|
+
}) => {
|
|
26
|
+
if (selected) return selectedBackground;
|
|
27
|
+
return index % 2 === 0 ? tableOddColor : tableEvenColor;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
test('even/odd stripes use listItem tokens, not status', () => {
|
|
31
|
+
const even = '#111111';
|
|
32
|
+
const odd = '#222222';
|
|
33
|
+
assert.equal(
|
|
34
|
+
resolveRowBackgroundColor({ selected: false, index: 0, tableOddColor: odd, tableEvenColor: even }),
|
|
35
|
+
odd,
|
|
36
|
+
);
|
|
37
|
+
assert.equal(
|
|
38
|
+
resolveRowBackgroundColor({ selected: false, index: 1, tableOddColor: odd, tableEvenColor: even }),
|
|
39
|
+
even,
|
|
40
|
+
);
|
|
41
|
+
assert.equal(
|
|
42
|
+
resolveRowBackgroundColor({ selected: true, index: 0, tableOddColor: odd, tableEvenColor: even }),
|
|
43
|
+
'#ECFEFF',
|
|
44
|
+
);
|
|
45
|
+
});
|