awing-library 2.1.2-dev.572 → 2.1.2-dev.573
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/dist/AWING/DataForm/container.d.ts.map +1 -1
- package/dist/AWING/DataForm/container.js +7 -2
- package/dist/AWING/DataForm/container.test.js +264 -34
- package/dist/AWING/DataForm/index.d.ts.map +1 -1
- package/dist/AWING/DataForm/index.js +2 -7
- package/dist/AWING/DataInput/components/AsyncAutocompleteInput.d.ts.map +1 -1
- package/dist/AWING/DataInput/components/AsyncAutocompleteInput.js +2 -1
- package/dist/AWING/DataInput/components/AutocompleteInput.d.ts.map +1 -1
- package/dist/AWING/DataInput/components/AutocompleteInput.js +1 -1
- package/dist/AWING/DataInput/components/DateTimeInput.d.ts.map +1 -1
- package/dist/AWING/DataInput/components/DateTimeInput.js +4 -1
- package/dist/AWING/DataInput/components/LazyAutocompleteInput.d.ts.map +1 -1
- package/dist/AWING/DataInput/components/LazyAutocompleteInput.js +1 -1
- package/dist/AWING/DataInput/components/MonthTimeInput.d.ts.map +1 -1
- package/dist/AWING/DataInput/components/MonthTimeInput.js +4 -1
- package/dist/AWING/DataInput/components/NumberInput.d.ts +7 -1
- package/dist/AWING/DataInput/components/NumberInput.d.ts.map +1 -1
- package/dist/AWING/DataInput/components/NumberInput.js +36 -7
- package/dist/AWING/DataInput/interfaces.d.ts +1 -1
- package/dist/AWING/DataInput/interfaces.d.ts.map +1 -1
- package/dist/AWING/DataInput2/components/NumberInput.d.ts.map +1 -1
- package/dist/AWING/DataInput2/components/NumberInput.js +12 -1
- package/dist/AWING/DirectoryPermission/AddOrEdit/PageContent.js +1 -1
- package/dist/AWING/NumberFormat/index.d.ts.map +1 -1
- package/dist/AWING/NumberFormat/index.js +22 -7
- package/package.json +1 -1
- package/dist/AWING/NumberFormat/index.test.js +0 -215
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../../src/AWING/DataForm/container.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../../src/AWING/DataForm/container.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,2CAsSzE"}
|
|
@@ -36,7 +36,9 @@ function DataForm(props) {
|
|
|
36
36
|
const isNewId = newId !== currentId;
|
|
37
37
|
const isNewVersion = propsOldValue?.versionId !== oldValueRef.current?.versionId;
|
|
38
38
|
const isContentDifferent = currentPropsStr !== lastPropsOldValueStr.current;
|
|
39
|
-
|
|
39
|
+
const versionId = propsOldValue?.versionId;
|
|
40
|
+
const hasExplicitInvalidVersion = void 0 !== versionId && versionId <= 0;
|
|
41
|
+
if (isFirstLoad || isNewId || isNewVersion || isContentDifferent && !hasExplicitInvalidVersion) {
|
|
40
42
|
lastPropsOldValueStr.current = currentPropsStr;
|
|
41
43
|
oldValueRef.current = propsOldValue;
|
|
42
44
|
setOldValue(propsOldValue);
|
|
@@ -155,7 +157,10 @@ function DataForm(props) {
|
|
|
155
157
|
disabled: fieldPermissions && void 0 !== fieldPermissions[fieldName] ? !fieldPermissions[fieldName] || fieldDef.disabled : fieldDef.disabled,
|
|
156
158
|
error: showError
|
|
157
159
|
};
|
|
158
|
-
fieldProps.onChange = (newValue, valid)=>
|
|
160
|
+
fieldProps.onChange = (newValue, valid)=>{
|
|
161
|
+
const safeValue = void 0 === newValue ? '' : newValue;
|
|
162
|
+
handleChange(fieldName, safeValue, valid ?? true);
|
|
163
|
+
};
|
|
159
164
|
fieldProps.value = fieldValue;
|
|
160
165
|
const { key, ...otherFieldProps } = fieldProps;
|
|
161
166
|
return /*#__PURE__*/ jsx(Grid, {
|
|
@@ -9,7 +9,7 @@ var __webpack_modules__ = {
|
|
|
9
9
|
"AWING/DataInput": function(module) {
|
|
10
10
|
module.exports = __WEBPACK_EXTERNAL_MODULE__DataInput_index_js_c7933a4f__;
|
|
11
11
|
},
|
|
12
|
-
"
|
|
12
|
+
"../helper": function(module) {
|
|
13
13
|
module.exports = __WEBPACK_EXTERNAL_MODULE__helper_js_663c9e82__;
|
|
14
14
|
}
|
|
15
15
|
};
|
|
@@ -92,9 +92,9 @@ jest.mock('@mui/material', ()=>({
|
|
|
92
92
|
})
|
|
93
93
|
}));
|
|
94
94
|
const mockInputFactory = __webpack_require__("AWING/DataInput")["default"];
|
|
95
|
-
const mockCalculateValue = __webpack_require__("
|
|
96
|
-
const mockConvertFormulaToBinaryTree = __webpack_require__("
|
|
97
|
-
const mockReplaceFieldsValue = __webpack_require__("
|
|
95
|
+
const mockCalculateValue = __webpack_require__("../helper").calculateValue;
|
|
96
|
+
const mockConvertFormulaToBinaryTree = __webpack_require__("../helper").convertFormulaToBinaryTree;
|
|
97
|
+
const mockReplaceFieldsValue = __webpack_require__("../helper").replaceFieldsValue;
|
|
98
98
|
describe('DataForm Component', ()=>{
|
|
99
99
|
const mockFields = [
|
|
100
100
|
{
|
|
@@ -245,7 +245,7 @@ describe('DataForm Component', ()=>{
|
|
|
245
245
|
}));
|
|
246
246
|
expect(onFormValid).toHaveBeenCalledWith(false);
|
|
247
247
|
});
|
|
248
|
-
test
|
|
248
|
+
test('calls onValidateForm when provided', ()=>{
|
|
249
249
|
const onValidateForm = jest.fn().mockReturnValue(true);
|
|
250
250
|
const onFormValid = jest.fn();
|
|
251
251
|
render(/*#__PURE__*/ jsx(container, {
|
|
@@ -254,7 +254,10 @@ describe('DataForm Component', ()=>{
|
|
|
254
254
|
onValidateForm: onValidateForm,
|
|
255
255
|
onFormValid: onFormValid
|
|
256
256
|
}));
|
|
257
|
-
expect(
|
|
257
|
+
expect(onValidateForm).toHaveBeenCalledWith(expect.objectContaining({
|
|
258
|
+
name: 'Jane Doe'
|
|
259
|
+
}));
|
|
260
|
+
expect(onFormValid).toHaveBeenCalledWith(true);
|
|
258
261
|
});
|
|
259
262
|
test('auto validates text fields when autoValidateFieldText is true', ()=>{
|
|
260
263
|
const textFields = [
|
|
@@ -275,7 +278,7 @@ describe('DataForm Component', ()=>{
|
|
|
275
278
|
}));
|
|
276
279
|
});
|
|
277
280
|
});
|
|
278
|
-
describe
|
|
281
|
+
describe('Callbacks', ()=>{
|
|
279
282
|
test('calls onUpdate when fields change', ()=>{
|
|
280
283
|
const onUpdate = jest.fn();
|
|
281
284
|
render(/*#__PURE__*/ jsx(container, {
|
|
@@ -289,9 +292,9 @@ describe('DataForm Component', ()=>{
|
|
|
289
292
|
value: 'Updated Name'
|
|
290
293
|
}
|
|
291
294
|
});
|
|
292
|
-
expect(onUpdate).toHaveBeenCalledWith({
|
|
295
|
+
expect(onUpdate).toHaveBeenCalledWith(expect.objectContaining({
|
|
293
296
|
name: 'Updated Name'
|
|
294
|
-
},
|
|
297
|
+
}), true, 'name');
|
|
295
298
|
});
|
|
296
299
|
test('calls onValues when fields change', ()=>{
|
|
297
300
|
const onValues = jest.fn();
|
|
@@ -306,42 +309,25 @@ describe('DataForm Component', ()=>{
|
|
|
306
309
|
value: 'Updated Name'
|
|
307
310
|
}
|
|
308
311
|
});
|
|
309
|
-
expect(onValues).toHaveBeenCalledWith({
|
|
310
|
-
...mockOldValue,
|
|
312
|
+
expect(onValues).toHaveBeenCalledWith(expect.objectContaining({
|
|
311
313
|
name: 'Updated Name'
|
|
312
|
-
},
|
|
314
|
+
}), true, 'name');
|
|
313
315
|
});
|
|
314
|
-
test('calls onFormValid
|
|
316
|
+
test('calls onFormValid on mount with complete oldValue', ()=>{
|
|
315
317
|
const onFormValid = jest.fn();
|
|
316
318
|
render(/*#__PURE__*/ jsx(container, {
|
|
317
319
|
fields: mockFields,
|
|
318
320
|
oldValue: mockOldValue,
|
|
319
321
|
onFormValid: onFormValid
|
|
320
322
|
}));
|
|
321
|
-
expect(onFormValid).toHaveBeenCalledWith(
|
|
323
|
+
expect(onFormValid).toHaveBeenCalledWith(true);
|
|
322
324
|
});
|
|
323
|
-
test('form
|
|
325
|
+
test('form remains valid after field changes with valid values', ()=>{
|
|
324
326
|
const onFormValid = jest.fn();
|
|
325
|
-
const onUpdate = jest.fn();
|
|
326
|
-
mockInputFactory.mockImplementation((props)=>/*#__PURE__*/ jsxs("div", {
|
|
327
|
-
"data-testid": `input-${props.fieldName}`,
|
|
328
|
-
children: [
|
|
329
|
-
/*#__PURE__*/ jsx("input", {
|
|
330
|
-
value: props.value || '',
|
|
331
|
-
onChange: (e)=>props.onChange(e.target.value, true),
|
|
332
|
-
"data-testid": `input-field-${props.fieldName}`
|
|
333
|
-
}),
|
|
334
|
-
props.error && /*#__PURE__*/ jsx("span", {
|
|
335
|
-
"data-testid": `error-${props.fieldName}`,
|
|
336
|
-
children: "Error"
|
|
337
|
-
})
|
|
338
|
-
]
|
|
339
|
-
}));
|
|
340
327
|
render(/*#__PURE__*/ jsx(container, {
|
|
341
328
|
fields: mockFields,
|
|
342
329
|
oldValue: mockOldValue,
|
|
343
|
-
onFormValid: onFormValid
|
|
344
|
-
onUpdate: onUpdate
|
|
330
|
+
onFormValid: onFormValid
|
|
345
331
|
}));
|
|
346
332
|
const nameInput = screen.getByTestId('input-field-name');
|
|
347
333
|
fireEvent.change(nameInput, {
|
|
@@ -591,7 +577,7 @@ describe('DataForm Component', ()=>{
|
|
|
591
577
|
expect(nameInput).not.toHaveAttribute('readonly');
|
|
592
578
|
expect(nameInput).not.toHaveAttribute('disabled');
|
|
593
579
|
});
|
|
594
|
-
test
|
|
580
|
+
test('does not set readOnly and disabled when fieldName not in fieldPermissions', ()=>{
|
|
595
581
|
const fields = [
|
|
596
582
|
{
|
|
597
583
|
fieldName: 'name',
|
|
@@ -763,7 +749,7 @@ describe('DataForm Component', ()=>{
|
|
|
763
749
|
oldValue: updatedOldValue
|
|
764
750
|
}));
|
|
765
751
|
const nameInput = screen.getByTestId('input-field-name');
|
|
766
|
-
expect(nameInput).toHaveValue('No Version');
|
|
752
|
+
expect(nameInput).toHaveValue('Still No Version');
|
|
767
753
|
});
|
|
768
754
|
test('updates fieldsToUpdate when field values differ from oldValue', ()=>{
|
|
769
755
|
const oldValueWithVersion = {
|
|
@@ -906,4 +892,248 @@ describe('DataForm Component', ()=>{
|
|
|
906
892
|
expect(ageInput).toHaveValue('30');
|
|
907
893
|
});
|
|
908
894
|
});
|
|
895
|
+
describe('Hợp đồng preserve null (nhóm 1)', ()=>{
|
|
896
|
+
test('field emit null → form state giữ nguyên null (không bị coalesce thành "")', ()=>{
|
|
897
|
+
const onUpdate = jest.fn();
|
|
898
|
+
mockInputFactory.mockImplementation((props)=>/*#__PURE__*/ jsx("div", {
|
|
899
|
+
"data-testid": `input-${props.fieldName}`,
|
|
900
|
+
children: /*#__PURE__*/ jsx("button", {
|
|
901
|
+
"data-testid": `clear-${props.fieldName}`,
|
|
902
|
+
onClick: ()=>props.onChange(null, true),
|
|
903
|
+
children: "Clear"
|
|
904
|
+
})
|
|
905
|
+
}));
|
|
906
|
+
render(/*#__PURE__*/ jsx(container, {
|
|
907
|
+
fields: mockFields,
|
|
908
|
+
oldValue: mockOldValue,
|
|
909
|
+
onUpdate: onUpdate
|
|
910
|
+
}));
|
|
911
|
+
fireEvent.click(screen.getByTestId('clear-age'));
|
|
912
|
+
expect(onUpdate).toHaveBeenCalledWith(expect.objectContaining({
|
|
913
|
+
age: null
|
|
914
|
+
}), expect.any(Boolean), 'age');
|
|
915
|
+
});
|
|
916
|
+
test('field emit undefined → form state fallback "" (giữ tương thích cũ)', ()=>{
|
|
917
|
+
const onUpdate = jest.fn();
|
|
918
|
+
mockInputFactory.mockImplementation((props)=>/*#__PURE__*/ jsx("div", {
|
|
919
|
+
"data-testid": `input-${props.fieldName}`,
|
|
920
|
+
children: /*#__PURE__*/ jsx("button", {
|
|
921
|
+
"data-testid": `undef-${props.fieldName}`,
|
|
922
|
+
onClick: ()=>props.onChange(void 0, true),
|
|
923
|
+
children: "Undef"
|
|
924
|
+
})
|
|
925
|
+
}));
|
|
926
|
+
render(/*#__PURE__*/ jsx(container, {
|
|
927
|
+
fields: mockFields,
|
|
928
|
+
oldValue: mockOldValue,
|
|
929
|
+
onUpdate: onUpdate
|
|
930
|
+
}));
|
|
931
|
+
fireEvent.click(screen.getByTestId('undef-name'));
|
|
932
|
+
expect(onUpdate).toHaveBeenCalledWith(expect.objectContaining({
|
|
933
|
+
name: ''
|
|
934
|
+
}), expect.any(Boolean), 'name');
|
|
935
|
+
});
|
|
936
|
+
test('field emit 0 → form state giữ 0 (không bị coalesce dù 0 là falsy)', ()=>{
|
|
937
|
+
const onUpdate = jest.fn();
|
|
938
|
+
mockInputFactory.mockImplementation((props)=>/*#__PURE__*/ jsx("div", {
|
|
939
|
+
"data-testid": `input-${props.fieldName}`,
|
|
940
|
+
children: /*#__PURE__*/ jsx("button", {
|
|
941
|
+
"data-testid": `zero-${props.fieldName}`,
|
|
942
|
+
onClick: ()=>props.onChange(0, true),
|
|
943
|
+
children: "Zero"
|
|
944
|
+
})
|
|
945
|
+
}));
|
|
946
|
+
render(/*#__PURE__*/ jsx(container, {
|
|
947
|
+
fields: mockFields,
|
|
948
|
+
oldValue: mockOldValue,
|
|
949
|
+
onUpdate: onUpdate
|
|
950
|
+
}));
|
|
951
|
+
fireEvent.click(screen.getByTestId('zero-age'));
|
|
952
|
+
expect(onUpdate).toHaveBeenCalledWith(expect.objectContaining({
|
|
953
|
+
age: 0
|
|
954
|
+
}), expect.any(Boolean), 'age');
|
|
955
|
+
});
|
|
956
|
+
test('field emit false → form state giữ false', ()=>{
|
|
957
|
+
const onUpdate = jest.fn();
|
|
958
|
+
mockInputFactory.mockImplementation((props)=>/*#__PURE__*/ jsx("div", {
|
|
959
|
+
"data-testid": `input-${props.fieldName}`,
|
|
960
|
+
children: /*#__PURE__*/ jsx("button", {
|
|
961
|
+
"data-testid": `false-${props.fieldName}`,
|
|
962
|
+
onClick: ()=>props.onChange(false, true),
|
|
963
|
+
children: "False"
|
|
964
|
+
})
|
|
965
|
+
}));
|
|
966
|
+
render(/*#__PURE__*/ jsx(container, {
|
|
967
|
+
fields: mockFields,
|
|
968
|
+
oldValue: mockOldValue,
|
|
969
|
+
onUpdate: onUpdate
|
|
970
|
+
}));
|
|
971
|
+
fireEvent.click(screen.getByTestId('false-name'));
|
|
972
|
+
expect(onUpdate).toHaveBeenCalledWith(expect.objectContaining({
|
|
973
|
+
name: false
|
|
974
|
+
}), expect.any(Boolean), 'name');
|
|
975
|
+
});
|
|
976
|
+
test('null đi qua onValues (completeValues) — vẫn giữ null', ()=>{
|
|
977
|
+
const onValues = jest.fn();
|
|
978
|
+
mockInputFactory.mockImplementation((props)=>/*#__PURE__*/ jsx("div", {
|
|
979
|
+
"data-testid": `input-${props.fieldName}`,
|
|
980
|
+
children: /*#__PURE__*/ jsx("button", {
|
|
981
|
+
"data-testid": `null-${props.fieldName}`,
|
|
982
|
+
onClick: ()=>props.onChange(null, true),
|
|
983
|
+
children: "Null"
|
|
984
|
+
})
|
|
985
|
+
}));
|
|
986
|
+
render(/*#__PURE__*/ jsx(container, {
|
|
987
|
+
fields: mockFields,
|
|
988
|
+
oldValue: mockOldValue,
|
|
989
|
+
onValues: onValues
|
|
990
|
+
}));
|
|
991
|
+
fireEvent.click(screen.getByTestId('null-age'));
|
|
992
|
+
expect(onValues).toHaveBeenCalledWith(expect.objectContaining({
|
|
993
|
+
age: null
|
|
994
|
+
}), expect.any(Boolean), 'age');
|
|
995
|
+
});
|
|
996
|
+
test('chuỗi rỗng "" emit từ field → form state vẫn ""', ()=>{
|
|
997
|
+
const onUpdate = jest.fn();
|
|
998
|
+
mockInputFactory.mockImplementation((props)=>/*#__PURE__*/ jsx("div", {
|
|
999
|
+
"data-testid": `input-${props.fieldName}`,
|
|
1000
|
+
children: /*#__PURE__*/ jsx("button", {
|
|
1001
|
+
"data-testid": `empty-${props.fieldName}`,
|
|
1002
|
+
onClick: ()=>props.onChange('', true),
|
|
1003
|
+
children: "Empty"
|
|
1004
|
+
})
|
|
1005
|
+
}));
|
|
1006
|
+
render(/*#__PURE__*/ jsx(container, {
|
|
1007
|
+
fields: mockFields,
|
|
1008
|
+
oldValue: mockOldValue,
|
|
1009
|
+
onUpdate: onUpdate
|
|
1010
|
+
}));
|
|
1011
|
+
fireEvent.click(screen.getByTestId('empty-name'));
|
|
1012
|
+
expect(onUpdate).toHaveBeenCalledWith(expect.objectContaining({
|
|
1013
|
+
name: ''
|
|
1014
|
+
}), expect.any(Boolean), 'name');
|
|
1015
|
+
});
|
|
1016
|
+
});
|
|
1017
|
+
describe('isNewId Branch (EFFECT 1 reset)', ()=>{
|
|
1018
|
+
test('resets toàn bộ state khi id thay đổi', ()=>{
|
|
1019
|
+
const oldValueV1 = {
|
|
1020
|
+
name: 'Alice',
|
|
1021
|
+
age: 20,
|
|
1022
|
+
email: 'alice@example.com',
|
|
1023
|
+
id: 1
|
|
1024
|
+
};
|
|
1025
|
+
const { rerender } = render(/*#__PURE__*/ jsx(container, {
|
|
1026
|
+
fields: mockFields,
|
|
1027
|
+
oldValue: oldValueV1
|
|
1028
|
+
}));
|
|
1029
|
+
const nameInput = screen.getByTestId('input-field-name');
|
|
1030
|
+
fireEvent.change(nameInput, {
|
|
1031
|
+
target: {
|
|
1032
|
+
value: 'Modified Alice'
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
1035
|
+
expect(nameInput).toHaveValue('Modified Alice');
|
|
1036
|
+
const oldValueV2 = {
|
|
1037
|
+
name: 'Bob',
|
|
1038
|
+
age: 30,
|
|
1039
|
+
email: 'bob@example.com',
|
|
1040
|
+
id: 2
|
|
1041
|
+
};
|
|
1042
|
+
rerender(/*#__PURE__*/ jsx(container, {
|
|
1043
|
+
fields: mockFields,
|
|
1044
|
+
oldValue: oldValueV2
|
|
1045
|
+
}));
|
|
1046
|
+
expect(nameInput).toHaveValue('Bob');
|
|
1047
|
+
expect(screen.getByTestId('input-field-age')).toHaveValue('30');
|
|
1048
|
+
});
|
|
1049
|
+
test('reset state khi id từ có thành không có (undefined → có)', ()=>{
|
|
1050
|
+
const oldValueNoId = {
|
|
1051
|
+
name: 'NoId',
|
|
1052
|
+
age: 10,
|
|
1053
|
+
email: 'noid@example.com'
|
|
1054
|
+
};
|
|
1055
|
+
const { rerender } = render(/*#__PURE__*/ jsx(container, {
|
|
1056
|
+
fields: mockFields,
|
|
1057
|
+
oldValue: oldValueNoId
|
|
1058
|
+
}));
|
|
1059
|
+
const oldValueWithId = {
|
|
1060
|
+
name: 'WithId',
|
|
1061
|
+
age: 99,
|
|
1062
|
+
email: 'withid@example.com',
|
|
1063
|
+
id: 5
|
|
1064
|
+
};
|
|
1065
|
+
rerender(/*#__PURE__*/ jsx(container, {
|
|
1066
|
+
fields: mockFields,
|
|
1067
|
+
oldValue: oldValueWithId
|
|
1068
|
+
}));
|
|
1069
|
+
expect(screen.getByTestId('input-field-name')).toHaveValue('WithId');
|
|
1070
|
+
});
|
|
1071
|
+
});
|
|
1072
|
+
describe('fieldDependencies', ()=>{
|
|
1073
|
+
test('xóa các field phụ thuộc khi field cha thay đổi', ()=>{
|
|
1074
|
+
const fieldsWithDep = [
|
|
1075
|
+
{
|
|
1076
|
+
fieldName: 'country',
|
|
1077
|
+
type: index_js_.FIELD_TYPE.SELECT,
|
|
1078
|
+
label: 'Country'
|
|
1079
|
+
},
|
|
1080
|
+
{
|
|
1081
|
+
fieldName: 'city',
|
|
1082
|
+
type: index_js_.FIELD_TYPE.TEXT,
|
|
1083
|
+
label: 'City'
|
|
1084
|
+
},
|
|
1085
|
+
{
|
|
1086
|
+
fieldName: 'district',
|
|
1087
|
+
type: index_js_.FIELD_TYPE.TEXT,
|
|
1088
|
+
label: 'District'
|
|
1089
|
+
}
|
|
1090
|
+
];
|
|
1091
|
+
const onUpdate = jest.fn();
|
|
1092
|
+
render(/*#__PURE__*/ jsx(container, {
|
|
1093
|
+
fields: fieldsWithDep,
|
|
1094
|
+
oldValue: {
|
|
1095
|
+
country: 'VN',
|
|
1096
|
+
city: 'HCM',
|
|
1097
|
+
district: 'Q1'
|
|
1098
|
+
},
|
|
1099
|
+
fieldDependencies: {
|
|
1100
|
+
country: [
|
|
1101
|
+
'city',
|
|
1102
|
+
'district'
|
|
1103
|
+
]
|
|
1104
|
+
},
|
|
1105
|
+
onUpdate: onUpdate
|
|
1106
|
+
}));
|
|
1107
|
+
const countryInput = screen.getByTestId('input-field-country');
|
|
1108
|
+
fireEvent.change(countryInput, {
|
|
1109
|
+
target: {
|
|
1110
|
+
value: 'US'
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
expect(onUpdate).toHaveBeenCalledWith(expect.objectContaining({
|
|
1114
|
+
country: 'US'
|
|
1115
|
+
}), expect.any(Boolean), 'country');
|
|
1116
|
+
const lastCall = onUpdate.mock.calls[onUpdate.mock.calls.length - 1][0];
|
|
1117
|
+
expect(lastCall.city).toBeUndefined();
|
|
1118
|
+
expect(lastCall.district).toBeUndefined();
|
|
1119
|
+
});
|
|
1120
|
+
test('không xóa field khi không có fieldDependencies', ()=>{
|
|
1121
|
+
const onUpdate = jest.fn();
|
|
1122
|
+
render(/*#__PURE__*/ jsx(container, {
|
|
1123
|
+
fields: mockFields,
|
|
1124
|
+
oldValue: mockOldValue,
|
|
1125
|
+
onUpdate: onUpdate
|
|
1126
|
+
}));
|
|
1127
|
+
const nameInput = screen.getByTestId('input-field-name');
|
|
1128
|
+
fireEvent.change(nameInput, {
|
|
1129
|
+
target: {
|
|
1130
|
+
value: 'Test'
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
expect(onUpdate).toHaveBeenCalledWith(expect.objectContaining({
|
|
1134
|
+
name: 'Test',
|
|
1135
|
+
age: 25
|
|
1136
|
+
}), expect.any(Boolean), 'name');
|
|
1137
|
+
});
|
|
1138
|
+
});
|
|
909
1139
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/AWING/DataForm/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/AWING/DataForm/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,2CAE1E;AAED,cAAc,aAAa,CAAC;AAC5B,eAAe,QAAQ,CAAC"}
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { LocalizationProvider } from "@mui/x-date-pickers";
|
|
3
|
-
import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment";
|
|
4
2
|
import container from "./container.js";
|
|
5
3
|
export * from "./interface.js";
|
|
6
4
|
function DataForm_DataForm(props) {
|
|
7
|
-
return /*#__PURE__*/ jsx(
|
|
8
|
-
|
|
9
|
-
children: /*#__PURE__*/ jsx(container, {
|
|
10
|
-
...props
|
|
11
|
-
})
|
|
5
|
+
return /*#__PURE__*/ jsx(container, {
|
|
6
|
+
...props
|
|
12
7
|
});
|
|
13
8
|
}
|
|
14
9
|
const DataForm = DataForm_DataForm;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AsyncAutocompleteInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput/components/AsyncAutocompleteInput.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAA0B,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE9E,MAAM,WAAW,gCAAgC,CAAC,CAAC,CAAE,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IAC/E,IAAI,EAAE,UAAU,CAAC,kBAAkB,GAAG,oBAAoB,CAAC;IAC3D,YAAY,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAClC,cAAc,CAAC,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,cAAc,CAAC,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC;IAC/B,WAAW,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACnC;AAED,eAAO,MAAM,sBAAsB,GAAI,CAAC,mBAAoB,gCAAgC,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"AsyncAutocompleteInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput/components/AsyncAutocompleteInput.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAA0B,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE9E,MAAM,WAAW,gCAAgC,CAAC,CAAC,CAAE,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IAC/E,IAAI,EAAE,UAAU,CAAC,kBAAkB,GAAG,oBAAoB,CAAC;IAC3D,YAAY,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAClC,cAAc,CAAC,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,cAAc,CAAC,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC;IAC/B,WAAW,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACnC;AAED,eAAO,MAAM,sBAAsB,GAAI,CAAC,mBAAoB,gCAAgC,CAAC,CAAC,CAAC,4CAyD9F,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
|
|
@@ -25,7 +25,8 @@ const AsyncAutocompleteInput_AsyncAutocompleteInput = (fieldDefinition)=>{
|
|
|
25
25
|
disableCloseOnSelect: multiple,
|
|
26
26
|
value: value,
|
|
27
27
|
onChange: (newValue)=>{
|
|
28
|
-
|
|
28
|
+
const result = newValue ?? null;
|
|
29
|
+
onChange && onChange(result, onValidate(result));
|
|
29
30
|
},
|
|
30
31
|
disabled: disabled,
|
|
31
32
|
TextFieldProps: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutocompleteInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput/components/AutocompleteInput.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,MAAM,WAAW,2BAA2B,CAAC,CAAC,CAAE,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IAC1E,IAAI,EAAE,UAAU,CAAC,YAAY,GAAG,cAAc,CAAC;IAC/C,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,eAAO,MAAM,iBAAiB,GAAI,CAAC,mBAAoB,2BAA2B,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"AutocompleteInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput/components/AutocompleteInput.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,MAAM,WAAW,2BAA2B,CAAC,CAAC,CAAE,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IAC1E,IAAI,EAAE,UAAU,CAAC,YAAY,GAAG,cAAc,CAAC;IAC/C,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,eAAO,MAAM,iBAAiB,GAAI,CAAC,mBAAoB,2BAA2B,CAAC,CAAC,CAAC,4CA2GpF,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -29,7 +29,7 @@ const AutocompleteInput = (fieldDefinition)=>{
|
|
|
29
29
|
noOptionsText: t('Common.NoOptions'),
|
|
30
30
|
loadingText: t('Common.Loading'),
|
|
31
31
|
onChange: (_e, value)=>{
|
|
32
|
-
const newValue = Array.isArray(value) ? value.map((x)=>x?.value) : value?.value;
|
|
32
|
+
const newValue = Array.isArray(value) ? value.map((x)=>x?.value) : value?.value ?? null;
|
|
33
33
|
onChange && onChange(newValue, onValidate(newValue));
|
|
34
34
|
},
|
|
35
35
|
value: multiple ? Array.isArray(value) ? value.map((v)=>options.find((x)=>x.value === v)).filter((x)=>void 0 !== x) : [] : options.find((x)=>x.value === value) ?? null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateTimeInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput/components/DateTimeInput.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,MAAM,WAAW,uBAAwB,SAAQ,mBAAmB,CAAC,IAAI,CAAC;IACtE,IAAI,EAAE,UAAU,CAAC,IAAI,GAAG,UAAU,CAAC,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IAC5D,YAAY,CAAC,EAAE,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,IAAI,CAAC;QACf,OAAO,CAAC,EAAE,IAAI,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,eAAO,MAAM,aAAa,oBAAqB,uBAAuB,
|
|
1
|
+
{"version":3,"file":"DateTimeInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput/components/DateTimeInput.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,MAAM,WAAW,uBAAwB,SAAQ,mBAAmB,CAAC,IAAI,CAAC;IACtE,IAAI,EAAE,UAAU,CAAC,IAAI,GAAG,UAAU,CAAC,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IAC5D,YAAY,CAAC,EAAE,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,IAAI,CAAC;QACf,OAAO,CAAC,EAAE,IAAI,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,eAAO,MAAM,aAAa,oBAAqB,uBAAuB,4CAqDrE,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -17,7 +17,10 @@ const DateTimeInput = (fieldDefinition)=>{
|
|
|
17
17
|
defaultValue: defaultValue ? moment(defaultValue) : null,
|
|
18
18
|
label: label,
|
|
19
19
|
value: value ? moment(value) : null,
|
|
20
|
-
onChange: (date)=>
|
|
20
|
+
onChange: (date)=>{
|
|
21
|
+
const result = date?.toDate() ?? null;
|
|
22
|
+
onChange && onChange(result, onValidate(result ?? void 0));
|
|
23
|
+
},
|
|
21
24
|
slotProps: {
|
|
22
25
|
textField: {
|
|
23
26
|
required: required,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LazyAutocompleteInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput/components/LazyAutocompleteInput.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAGlE,KAAK,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAC7B,MAAM,WAAW,+BAA+B,CAAC,CAAC,CAAE,SAAQ,IAAI,CAAC,2BAA2B,CAAC,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAChH,IAAI,EAAE,UAAU,CAAC,iBAAiB,GAAG,kBAAkB,CAAC;IACxD,YAAY,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACjD,iBAAiB,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;CAC3C;AAED,eAAO,MAAM,qBAAqB,GAAI,CAAC,mBAAoB,+BAA+B,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"LazyAutocompleteInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput/components/LazyAutocompleteInput.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAGlE,KAAK,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAC7B,MAAM,WAAW,+BAA+B,CAAC,CAAC,CAAE,SAAQ,IAAI,CAAC,2BAA2B,CAAC,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAChH,IAAI,EAAE,UAAU,CAAC,iBAAiB,GAAG,kBAAkB,CAAC;IACxD,YAAY,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACjD,iBAAiB,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;CAC3C;AAED,eAAO,MAAM,qBAAqB,GAAI,CAAC,mBAAoB,+BAA+B,CAAC,CAAC,CAAC,4CA4H5F,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
|
|
@@ -52,7 +52,7 @@ const LazyAutocompleteInput_LazyAutocompleteInput = (fieldDefinition)=>{
|
|
|
52
52
|
noOptionsText: fetched ? t('Common.NoOptions') : t('Common.Loading'),
|
|
53
53
|
loadingText: t('Common.Loading'),
|
|
54
54
|
onChange: (_e, value)=>{
|
|
55
|
-
const newValue = Array.isArray(value) ? value.map((x)=>x) : value;
|
|
55
|
+
const newValue = Array.isArray(value) ? value.map((x)=>x) : value ?? null;
|
|
56
56
|
onChange && onChange(newValue, onValidate(newValue));
|
|
57
57
|
},
|
|
58
58
|
value: multiple ? value ?? [] : value ?? null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MonthTimeInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput/components/MonthTimeInput.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,eAAO,MAAM,cAAc,oBAAqB,uBAAuB,
|
|
1
|
+
{"version":3,"file":"MonthTimeInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput/components/MonthTimeInput.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,eAAO,MAAM,cAAc,oBAAqB,uBAAuB,4CA6CtE,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -21,7 +21,10 @@ const MonthTimeInput = (fieldDefinition)=>{
|
|
|
21
21
|
label: label,
|
|
22
22
|
value: moment(value, 'MM-YYYY'),
|
|
23
23
|
disabled: disabled,
|
|
24
|
-
onChange: (date)=>
|
|
24
|
+
onChange: (date)=>{
|
|
25
|
+
const result = date?.toDate() ?? null;
|
|
26
|
+
onChange && onChange(result, onValidate(result ?? void 0));
|
|
27
|
+
},
|
|
25
28
|
readOnly: readOnly,
|
|
26
29
|
slotProps: {
|
|
27
30
|
textField: {
|
|
@@ -6,7 +6,13 @@ export interface NumberFieldDefinition extends BaseFieldDefinition<number> {
|
|
|
6
6
|
autoFormula?: string;
|
|
7
7
|
min?: number;
|
|
8
8
|
max?: number;
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Cho phép xoá trắng ô input.
|
|
11
|
+
* - `true` (mặc định): xoá trắng → onChange trả về `null`.
|
|
12
|
+
* - `false`: xoá trắng → fallback về `clampNumber(0, min, max)`.
|
|
13
|
+
*/
|
|
14
|
+
allowEmpty?: boolean;
|
|
15
|
+
onChange?(newValue?: number | null, valid?: boolean, actualValue?: number | null): void;
|
|
10
16
|
}
|
|
11
17
|
export declare function clampNumber(value: number, min?: number, max?: number): number;
|
|
12
18
|
export declare const NumberInput: (fieldDefinition: NumberFieldDefinition) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NumberInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput/components/NumberInput.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB,CAAC,MAAM,CAAC;IACtE,IAAI,EAAE,UAAU,CAAC,MAAM,GAAG,QAAQ,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"NumberInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput/components/NumberInput.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB,CAAC,MAAM,CAAC;IACtE,IAAI,EAAE,UAAU,CAAC,MAAM,GAAG,QAAQ,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;CAC3F;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,UAOpE;AAED,eAAO,MAAM,WAAW,oBAAqB,qBAAqB,4CAkIjE,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -14,25 +14,54 @@ const NumberInput = (fieldDefinition)=>{
|
|
|
14
14
|
const { t } = useTranslation(void 0, {
|
|
15
15
|
i18n: i18n
|
|
16
16
|
});
|
|
17
|
-
const { name, defaultValue, value, onChange, error, disableHelperText, helperText, min, max, onValidateCustom, ...other } = fieldDefinition;
|
|
18
|
-
const [valueInput, setValueInput] = useState(defaultValue
|
|
17
|
+
const { name, defaultValue, value, onChange, error, disableHelperText, helperText, min, max, onValidateCustom, allowEmpty = true, ...other } = fieldDefinition;
|
|
18
|
+
const [valueInput, setValueInput] = useState(defaultValue ?? value ?? '');
|
|
19
19
|
const isComposing = useRef(false);
|
|
20
20
|
useEffect(()=>{
|
|
21
|
-
if (
|
|
21
|
+
if (isComposing.current) return;
|
|
22
|
+
const isIntermediateDecimal = 'string' == typeof valueInput && valueInput.endsWith('.') && Number(valueInput.slice(0, -1) || '0') === value;
|
|
23
|
+
if (!isIntermediateDecimal && valueInput !== value) setValueInput(value ?? '');
|
|
22
24
|
}, [
|
|
23
25
|
value
|
|
24
26
|
]);
|
|
25
27
|
const onValidate = (val)=>{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
const isEmpty = '' === val || null == val;
|
|
29
|
+
if (onValidateCustom) return onValidateCustom(isEmpty ? void 0 : Number(val));
|
|
30
|
+
if (isEmpty) return !fieldDefinition.required;
|
|
31
|
+
const n = Number(val);
|
|
32
|
+
return (void 0 === min || n >= min) && (void 0 === max || n <= max) && numberNotNullValid(n);
|
|
28
33
|
};
|
|
29
34
|
const processChange = (newValue)=>{
|
|
30
|
-
if ('-' === newValue || '' === newValue)
|
|
35
|
+
if ('-' === newValue || '' === newValue) {
|
|
36
|
+
if ('-' === newValue && void 0 !== min && min >= 0) {
|
|
37
|
+
setValueInput(0);
|
|
38
|
+
if (!isComposing.current) onChange?.(0, onValidate(0), 0);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (allowEmpty) {
|
|
42
|
+
setValueInput(newValue);
|
|
43
|
+
if (!isComposing.current) onChange?.(null, onValidate(null), null);
|
|
44
|
+
} else {
|
|
45
|
+
const fallback = clampNumber(0, min, max);
|
|
46
|
+
setValueInput(fallback);
|
|
47
|
+
if (!isComposing.current) onChange?.(fallback, onValidate(fallback), fallback);
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (newValue.endsWith('.')) {
|
|
52
|
+
setValueInput(newValue);
|
|
53
|
+
if (!isComposing.current) {
|
|
54
|
+
const intPart = newValue.slice(0, -1);
|
|
55
|
+
const intNumber = '' === intPart || '-' === intPart ? null : clampNumber(Number(intPart), min, max);
|
|
56
|
+
onChange?.(intNumber, onValidate(intNumber), intNumber);
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
31
60
|
const numberValue = Number(newValue);
|
|
32
61
|
if (!isNaN(numberValue)) {
|
|
33
62
|
const currentValue = clampNumber(numberValue, min, max);
|
|
34
63
|
setValueInput(currentValue);
|
|
35
|
-
if (!isComposing.current) onChange
|
|
64
|
+
if (!isComposing.current) onChange?.(currentValue, onValidate(currentValue), numberValue);
|
|
36
65
|
}
|
|
37
66
|
};
|
|
38
67
|
const handleChange = (event)=>{
|
|
@@ -28,7 +28,7 @@ export interface BaseFieldDefinition<T> extends Omit<BaseTextFieldProps, 'value'
|
|
|
28
28
|
type?: string;
|
|
29
29
|
label?: string;
|
|
30
30
|
customeFieldChange?(fieldValue: any): Partial<T>;
|
|
31
|
-
onChange?(newValue?: T | undefined, valid?: boolean | undefined): void;
|
|
31
|
+
onChange?(newValue?: T | null | undefined, valid?: boolean | undefined): void;
|
|
32
32
|
fieldName: string;
|
|
33
33
|
onValidateCustom?(value?: T): boolean;
|
|
34
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../../src/AWING/DataInput/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,gCAAgC,EAAE,MAAM,qCAAqC,CAAC;AACvF,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,+BAA+B,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,EAAE,oCAAoC,EAAE,MAAM,yCAAyC,CAAC;AAC/F,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,8BAA8B,EAAE,MAAM,wCAAwC,CAAC;AACxF,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AAE9E,MAAM,WAAW,mBAAmB,CAAC,CAAC,CAAE,SAAQ,IAAI,CAAC,kBAAkB,EAAE,OAAO,CAAC;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,CAAC,UAAU,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACjD,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../../src/AWING/DataInput/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,gCAAgC,EAAE,MAAM,qCAAqC,CAAC;AACvF,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,+BAA+B,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,EAAE,oCAAoC,EAAE,MAAM,yCAAyC,CAAC;AAC/F,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,8BAA8B,EAAE,MAAM,wCAAwC,CAAC;AACxF,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AAE9E,MAAM,WAAW,mBAAmB,CAAC,CAAC,CAAE,SAAQ,IAAI,CAAC,kBAAkB,EAAE,OAAO,CAAC;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,CAAC,UAAU,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACjD,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC5B,MAAM,IAAI,SAAS,CAAC;CACvB;AAED,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IACpD,gCAAgC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAC5C,2BAA2B,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GACvC,+BAA+B,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAC3C,oCAAoC,GACpC,yBAAyB,GACzB,+BAA+B,GAC/B,uBAAuB,GACvB,uBAAuB,GACvB,wBAAwB,GACxB,qBAAqB,GACrB,oBAAoB,GACpB,qBAAqB,GACrB,mBAAmB,GACnB,oBAAoB,GACpB,2BAA2B,GAC3B,2BAA2B,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GACvC,8BAA8B,GAC9B,qBAAqB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NumberInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput2/components/NumberInput.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB,CAAC,MAAM,CAAC;IACtE,IAAI,EAAE,UAAU,CAAC,MAAM,GAAG,QAAQ,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7E;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,UAKpE;
|
|
1
|
+
{"version":3,"file":"NumberInput.d.ts","sourceRoot":"","sources":["../../../../src/AWING/DataInput2/components/NumberInput.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB,CAAC,MAAM,CAAC;IACtE,IAAI,EAAE,UAAU,CAAC,MAAM,GAAG,QAAQ,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7E;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,UAKpE;AAwED,eAAO,MAAM,WAAW,wDAtEmB,qBAAqB,6CAsE4B,CAAC;AAG7F,eAAe,WAAW,CAAC"}
|
|
@@ -24,11 +24,22 @@ const NumberInputInner = (fieldDefinition)=>{
|
|
|
24
24
|
]);
|
|
25
25
|
const onValidate = (val)=>{
|
|
26
26
|
if (onValidateCustom) return onValidateCustom(Number(val));
|
|
27
|
+
if (fieldDefinition.required && ('' === val || null == val)) return false;
|
|
27
28
|
return (void 0 === min || Number(val) >= min) && (void 0 === max || Number(val) <= max) && numberNotNullValid(Number(val));
|
|
28
29
|
};
|
|
29
30
|
const handleChange = (event)=>{
|
|
30
31
|
const newValue = event.target.value;
|
|
31
|
-
if ('-' === newValue || '' === newValue)
|
|
32
|
+
if ('-' === newValue || '' === newValue) {
|
|
33
|
+
if ('-' === newValue && void 0 !== min && min >= 0) {
|
|
34
|
+
setValueInput(0);
|
|
35
|
+
onChange?.(0, onValidate(0), 0);
|
|
36
|
+
} else {
|
|
37
|
+
setValueInput(newValue);
|
|
38
|
+
const isValid = !fieldDefinition.required;
|
|
39
|
+
onChange?.(void 0, isValid, void 0);
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
32
43
|
const numberValue = Number(newValue);
|
|
33
44
|
if (!isNaN(numberValue)) {
|
|
34
45
|
const currentValue = clampNumber(numberValue, min, max);
|
|
@@ -10,7 +10,7 @@ import PermissionTable from "../components/PermissionTable.js";
|
|
|
10
10
|
import AddOrEditHeader from "../components/AddOrEditHeader.js";
|
|
11
11
|
import { useAtomValue } from "jotai";
|
|
12
12
|
import { useGetDirectoryContext } from "../../../Features/SYSTEM/Directory/context.js";
|
|
13
|
-
import { WorkspaceType } from "../../../
|
|
13
|
+
import { WorkspaceType } from "../../../Commons/Enums.js";
|
|
14
14
|
function PageContent(props) {
|
|
15
15
|
const { t } = useTranslation();
|
|
16
16
|
const { explicitMatrixPermissions, inheritedMatrixPermissions, onExplicitMatrixPermissionsChange, isCreate, objectTypeCodeSelected, onDeleteAuthen, disableSelectSchema, explicitPermissions, onExplicitPermissionsChange, inheritedPermissions, authenPermissions, onChangeObjectTypeCode, onDrawerLevelChange, isFile } = props;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/AWING/NumberFormat/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAa,cAAc,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/AWING/NumberFormat/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAa,cAAc,EAAE,MAAM,eAAe,CAAC;AAkB1D,eAAO,MAAM,YAAY,UAAW,cAAc,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,4CA+DpE,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -2,9 +2,20 @@ import { jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { TextField } from "@mui/material";
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
4
|
import { formatNumberByLocale } from "../../Helpers/number.js";
|
|
5
|
+
function resolveDecimalSeparator(locale) {
|
|
6
|
+
try {
|
|
7
|
+
return new Intl.NumberFormat(locale).formatToParts(1.1).find((p)=>'decimal' === p.type)?.value ?? '.';
|
|
8
|
+
} catch {
|
|
9
|
+
return '.';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function escapeRegExp(str) {
|
|
13
|
+
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
|
|
14
|
+
}
|
|
5
15
|
const NumberFormat = (props)=>{
|
|
6
16
|
const { value, onChange, min, ...other } = props;
|
|
7
17
|
const { i18n } = useTranslation();
|
|
18
|
+
const decimalSeparator = resolveDecimalSeparator(i18n.language);
|
|
8
19
|
const handleChange = (event)=>{
|
|
9
20
|
const el = event.target;
|
|
10
21
|
let newValue = el.value;
|
|
@@ -14,24 +25,28 @@ const NumberFormat = (props)=>{
|
|
|
14
25
|
onChange(event);
|
|
15
26
|
}
|
|
16
27
|
};
|
|
17
|
-
function escapeRegExp(str) {
|
|
18
|
-
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
|
|
19
|
-
}
|
|
20
28
|
function getFloatString(num = '') {
|
|
21
|
-
const decimalSeparator = 'vi' === i18n.language ? ',' : '.';
|
|
22
29
|
const hasNegation = '-' === num[0];
|
|
23
30
|
if (hasNegation) num = num.replace('-', '');
|
|
24
31
|
const numRegex = new RegExp('[0-9]|' + escapeRegExp(decimalSeparator), 'g');
|
|
25
32
|
num = (num.match(numRegex) || []).join('').replace(decimalSeparator, '.');
|
|
26
33
|
const firstDecimalIndex = num.indexOf('.');
|
|
27
|
-
if (-1 !== firstDecimalIndex) num = `${num.substring(0, firstDecimalIndex)}.${num.substring(firstDecimalIndex + 1
|
|
28
|
-
if (hasNegation && 0
|
|
34
|
+
if (-1 !== firstDecimalIndex) num = `${num.substring(0, firstDecimalIndex)}.${num.substring(firstDecimalIndex + 1).replace(new RegExp(escapeRegExp(decimalSeparator), 'g'), '')}`;
|
|
35
|
+
if (hasNegation && (void 0 === min || min < 0)) num = '-' + num;
|
|
29
36
|
return num;
|
|
30
37
|
}
|
|
31
38
|
function getDisplayString(val) {
|
|
32
39
|
if (null == val || '' === val) return '';
|
|
33
40
|
if ('-' === val) return '-';
|
|
34
|
-
|
|
41
|
+
const str = String(val);
|
|
42
|
+
if (str.endsWith('.')) {
|
|
43
|
+
const n = Number(str.slice(0, -1) || '0');
|
|
44
|
+
if (Number.isNaN(n)) return '';
|
|
45
|
+
return formatNumberByLocale(n) + decimalSeparator;
|
|
46
|
+
}
|
|
47
|
+
const n = Number(val);
|
|
48
|
+
if (Number.isNaN(n)) return '';
|
|
49
|
+
return formatNumberByLocale(n);
|
|
35
50
|
}
|
|
36
51
|
return /*#__PURE__*/ jsx(TextField, {
|
|
37
52
|
value: getDisplayString(value),
|
package/package.json
CHANGED
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import "@testing-library/jest-dom";
|
|
3
|
-
import { fireEvent, render, screen } from "@testing-library/react";
|
|
4
|
-
import index from "./index.js";
|
|
5
|
-
import { AwingContext } from "../../Context/index.js";
|
|
6
|
-
jest.mock('../helper', ()=>({
|
|
7
|
-
formatNumberWithLanguage: (value, language)=>{
|
|
8
|
-
if (!value) return '0';
|
|
9
|
-
const numStr = String(value);
|
|
10
|
-
if ('vi' === language) return numStr.replace('.', ',');
|
|
11
|
-
return numStr;
|
|
12
|
-
}
|
|
13
|
-
}));
|
|
14
|
-
describe('NumberFormat Component', ()=>{
|
|
15
|
-
const mockOnChange = jest.fn();
|
|
16
|
-
const renderWithContext = (props = {}, language = 'en')=>render(/*#__PURE__*/ jsx(AwingContext.Provider, {
|
|
17
|
-
value: {
|
|
18
|
-
i18next: {
|
|
19
|
-
language
|
|
20
|
-
},
|
|
21
|
-
routes: [],
|
|
22
|
-
appHelper: {},
|
|
23
|
-
service: {},
|
|
24
|
-
transactionType: {}
|
|
25
|
-
},
|
|
26
|
-
children: /*#__PURE__*/ jsx(index, {
|
|
27
|
-
onChange: mockOnChange,
|
|
28
|
-
...props
|
|
29
|
-
})
|
|
30
|
-
}));
|
|
31
|
-
beforeEach(()=>{
|
|
32
|
-
mockOnChange.mockClear();
|
|
33
|
-
});
|
|
34
|
-
describe('Input Formatting', ()=>{
|
|
35
|
-
test('formats number input correctly for English locale', ()=>{
|
|
36
|
-
renderWithContext({
|
|
37
|
-
value: '1234.56'
|
|
38
|
-
});
|
|
39
|
-
const input = screen.getByRole('textbox');
|
|
40
|
-
expect(input).toHaveValue('1234.56');
|
|
41
|
-
});
|
|
42
|
-
test('formats number input correctly for Vietnamese locale', ()=>{
|
|
43
|
-
renderWithContext({
|
|
44
|
-
value: '1234.56'
|
|
45
|
-
}, 'vi');
|
|
46
|
-
const input = screen.getByRole('textbox');
|
|
47
|
-
expect(input).toHaveValue('1234,56');
|
|
48
|
-
});
|
|
49
|
-
test('handles empty value', ()=>{
|
|
50
|
-
renderWithContext({
|
|
51
|
-
value: ''
|
|
52
|
-
});
|
|
53
|
-
const input = screen.getByRole('textbox');
|
|
54
|
-
expect(input).toHaveValue('0');
|
|
55
|
-
});
|
|
56
|
-
test('handles null value', ()=>{
|
|
57
|
-
renderWithContext({
|
|
58
|
-
value: null
|
|
59
|
-
});
|
|
60
|
-
const input = screen.getByRole('textbox');
|
|
61
|
-
expect(input).toHaveValue('0');
|
|
62
|
-
});
|
|
63
|
-
test('handles undefined value', ()=>{
|
|
64
|
-
renderWithContext({
|
|
65
|
-
value: void 0
|
|
66
|
-
});
|
|
67
|
-
const input = screen.getByRole('textbox');
|
|
68
|
-
expect(input).toHaveValue('0');
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
describe('Input Handling', ()=>{
|
|
72
|
-
test('handles negative numbers when min is not 0', ()=>{
|
|
73
|
-
renderWithContext({
|
|
74
|
-
min: -100
|
|
75
|
-
});
|
|
76
|
-
const input = screen.getByRole('textbox');
|
|
77
|
-
fireEvent.change(input, {
|
|
78
|
-
target: {
|
|
79
|
-
value: '-123.45'
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
expect(mockOnChange).toHaveBeenCalled();
|
|
83
|
-
});
|
|
84
|
-
test('handles negative numbers when value is undefined', ()=>{
|
|
85
|
-
renderWithContext();
|
|
86
|
-
const input = screen.getByRole('textbox');
|
|
87
|
-
fireEvent.change(input, {
|
|
88
|
-
target: {
|
|
89
|
-
value: null
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
expect(mockOnChange).toHaveBeenCalled();
|
|
93
|
-
});
|
|
94
|
-
test('removes negative sign when min is 0', ()=>{
|
|
95
|
-
renderWithContext({
|
|
96
|
-
min: 0
|
|
97
|
-
});
|
|
98
|
-
const input = screen.getByRole('textbox');
|
|
99
|
-
fireEvent.change(input, {
|
|
100
|
-
target: {
|
|
101
|
-
value: '-123.45'
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
expect(mockOnChange).toHaveBeenCalled();
|
|
105
|
-
});
|
|
106
|
-
test('handles multiple decimal points correctly', ()=>{
|
|
107
|
-
renderWithContext();
|
|
108
|
-
const input = screen.getByRole('textbox');
|
|
109
|
-
fireEvent.change(input, {
|
|
110
|
-
target: {
|
|
111
|
-
value: '123.45.67'
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
expect(mockOnChange).toHaveBeenCalled();
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
describe('Special Characters', ()=>{
|
|
118
|
-
test('removes non-numeric characters except decimal separator', ()=>{
|
|
119
|
-
renderWithContext();
|
|
120
|
-
const input = screen.getByRole('textbox');
|
|
121
|
-
fireEvent.change(input, {
|
|
122
|
-
target: {
|
|
123
|
-
value: 'abc123.45xyz'
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
expect(mockOnChange).toHaveBeenCalled();
|
|
127
|
-
});
|
|
128
|
-
test('handles special characters in regex correctly', ()=>{
|
|
129
|
-
renderWithContext();
|
|
130
|
-
const input = screen.getByRole('textbox');
|
|
131
|
-
fireEvent.change(input, {
|
|
132
|
-
target: {
|
|
133
|
-
value: '123$%^&*.45'
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
expect(mockOnChange).toHaveBeenCalled();
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
describe('Decimal Separator Handling', ()=>{
|
|
140
|
-
test('converts Vietnamese decimal separator to dot for internal value', ()=>{
|
|
141
|
-
renderWithContext({}, 'vi');
|
|
142
|
-
const input = screen.getByRole('textbox');
|
|
143
|
-
fireEvent.change(input, {
|
|
144
|
-
target: {
|
|
145
|
-
value: '123,45'
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
expect(mockOnChange).toHaveBeenCalled();
|
|
149
|
-
const event = mockOnChange.mock.calls[0][0];
|
|
150
|
-
expect(event.target.value).toBe('0');
|
|
151
|
-
});
|
|
152
|
-
test('maintains decimal separator at the end of input', ()=>{
|
|
153
|
-
renderWithContext();
|
|
154
|
-
const input = screen.getByRole('textbox');
|
|
155
|
-
fireEvent.change(input, {
|
|
156
|
-
target: {
|
|
157
|
-
value: '123.'
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
expect(mockOnChange).toHaveBeenCalled();
|
|
161
|
-
});
|
|
162
|
-
test('-', ()=>{
|
|
163
|
-
renderWithContext({
|
|
164
|
-
value: '-'
|
|
165
|
-
});
|
|
166
|
-
const input = screen.getByRole('textbox');
|
|
167
|
-
fireEvent.change(input, {
|
|
168
|
-
target: {
|
|
169
|
-
value: '424'
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
const event = mockOnChange.mock.calls[0][0];
|
|
173
|
-
expect(event.target.value).toBe('-');
|
|
174
|
-
});
|
|
175
|
-
test('Số thập với tiếng Việt', ()=>{
|
|
176
|
-
renderWithContext({
|
|
177
|
-
value: '123.',
|
|
178
|
-
language: 'vi'
|
|
179
|
-
});
|
|
180
|
-
const input = screen.getByRole('textbox');
|
|
181
|
-
fireEvent.change(input, {
|
|
182
|
-
target: {
|
|
183
|
-
value: '123.'
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
const event = mockOnChange.mock.calls[0][0];
|
|
187
|
-
expect(event.target.value).toBe('123..');
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
describe('Props Handling', ()=>{
|
|
191
|
-
test('passes through additional TextField props', ()=>{
|
|
192
|
-
renderWithContext({
|
|
193
|
-
placeholder: 'Enter number',
|
|
194
|
-
disabled: true
|
|
195
|
-
});
|
|
196
|
-
const input = screen.getByRole('textbox');
|
|
197
|
-
expect(input).toHaveAttribute('placeholder', 'Enter number');
|
|
198
|
-
expect(input).toBeDisabled();
|
|
199
|
-
});
|
|
200
|
-
test('maintains type as text', ()=>{
|
|
201
|
-
renderWithContext({
|
|
202
|
-
type: 'number'
|
|
203
|
-
});
|
|
204
|
-
const input = screen.getByRole('textbox');
|
|
205
|
-
expect(input).toHaveAttribute('type', 'text');
|
|
206
|
-
});
|
|
207
|
-
test('maintains type as text', ()=>{
|
|
208
|
-
renderWithContext({
|
|
209
|
-
type: 'number'
|
|
210
|
-
});
|
|
211
|
-
const input = screen.getByRole('textbox');
|
|
212
|
-
expect(input).toHaveAttribute('type', 'text');
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
});
|