@truedat/df 8.13.5 → 8.13.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/components/DynamicForm.js +3 -0
- package/src/components/DynamicFormWithTranslations.js +3 -0
- package/src/components/FieldGroupSegment.js +3 -0
- package/src/components/FieldGroupWithTranslations.js +13 -0
- package/src/components/SelectDynamicFormWithTranslations.js +7 -1
- package/src/components/SelectableDynamicForm.js +7 -1
- package/src/components/__tests__/__snapshots__/SelectableDynamicForm.spec.js.snap +18 -1
- package/src/components/__tests__/originalContentFlow.spec.js +131 -0
- package/src/components/widgets/DateField.js +18 -9
- package/src/components/widgets/DateTimeField.js +26 -9
- package/src/components/widgets/DynamicField.js +54 -1
- package/src/components/widgets/DynamicTableField.js +41 -7
- package/src/components/widgets/FieldByWidget.js +36 -6
- package/src/components/widgets/__tests__/dateBoundsByWidget.spec.js +133 -0
- package/src/components/widgets/__tests__/dateBoundsEndToEnd.spec.js +83 -0
- package/src/components/widgets/__tests__/datePickerBoundaries.spec.js +124 -0
- package/src/components/widgets/__tests__/dateValueValidation.spec.js +215 -0
- package/src/components/widgets/__tests__/existingDateRestrictions.spec.js +354 -0
- package/src/templates/components/templateForm/DateRestrictionsForm.js +217 -0
- package/src/templates/components/templateForm/DefaultValue.js +79 -11
- package/src/templates/components/templateForm/FieldDefinition.js +15 -1
- package/src/templates/components/templateForm/TemplateForm.js +5 -0
- package/src/templates/components/templateForm/ValuesField.js +7 -0
- package/src/templates/components/templateForm/__tests__/FieldDefinition.spec.js +48 -0
- package/src/templates/components/templateForm/__tests__/TemplateForm.spec.js +107 -0
- package/src/templates/components/templateForm/__tests__/dateRestrictionsForm.spec.js +429 -0
- package/src/templates/components/templateForm/__tests__/dateRestrictionsValidation.spec.js +166 -0
- package/src/templates/components/templateForm/__tests__/defaultValueBounds.spec.js +191 -0
- package/src/templates/components/templateForm/__tests__/defaultValueRestrictions.spec.js +175 -0
- package/src/templates/components/templateForm/contentValidation.js +184 -0
- package/src/templates/utils/__tests__/dateRestrictions.spec.js +578 -0
- package/src/templates/utils/__tests__/originalTableRows.spec.js +129 -0
- package/src/templates/utils/__tests__/validateContentDateRestrictions.spec.js +519 -0
- package/src/templates/utils/applyTemplate.js +27 -20
- package/src/templates/utils/dateRestrictions.js +497 -0
- package/src/templates/utils/dateValueUnchanged.js +12 -0
- package/src/templates/utils/originalTableRows.js +41 -0
- package/src/templates/utils/validateContent.js +75 -4
|
@@ -1,7 +1,183 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
+
import {
|
|
3
|
+
resolveRestrictions,
|
|
4
|
+
checkValuesAgainstBounds,
|
|
5
|
+
} from "../../utils/dateRestrictions";
|
|
2
6
|
|
|
3
7
|
const arrayAnd = (list) => _.indexOf(true)(list) >= 0;
|
|
4
8
|
|
|
9
|
+
const isDateField = (field) =>
|
|
10
|
+
field.type === "date" || field.type === "datetime";
|
|
11
|
+
|
|
12
|
+
const RESTRICTION_UNITS = ["days", "weeks", "months", "years"];
|
|
13
|
+
const RESTRICTION_ERRORS = {
|
|
14
|
+
required: "template.field.restrictions.validation.required",
|
|
15
|
+
invalidDate: "template.field.restrictions.validation.invalid_date",
|
|
16
|
+
invalidAmount: "template.field.restrictions.validation.invalid_amount",
|
|
17
|
+
invalidUnit: "template.field.restrictions.validation.invalid_unit",
|
|
18
|
+
invalidOrder: "template.field.restrictions.validation.invalid_order",
|
|
19
|
+
invalidMode: "template.field.restrictions.validation.invalid_mode",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const isInteger = (value) =>
|
|
23
|
+
Number.isInteger(value) ||
|
|
24
|
+
(typeof value === "string" && /^-?\d+$/.test(value.trim()));
|
|
25
|
+
|
|
26
|
+
const isValidCalendarDate = (value, allowDateTime) => {
|
|
27
|
+
if (typeof value !== "string" || !value) return false;
|
|
28
|
+
const match =
|
|
29
|
+
/^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/.exec(
|
|
30
|
+
value,
|
|
31
|
+
);
|
|
32
|
+
if (!match || (!allowDateTime && match[4] !== undefined)) return false;
|
|
33
|
+
|
|
34
|
+
const [, rawYear, rawMonth, rawDay, rawHour, rawMinute, rawSecond] = match;
|
|
35
|
+
const year = Number(rawYear);
|
|
36
|
+
const month = Number(rawMonth);
|
|
37
|
+
const day = Number(rawDay);
|
|
38
|
+
const hour = rawHour === undefined ? 0 : Number(rawHour);
|
|
39
|
+
const minute = rawMinute === undefined ? 0 : Number(rawMinute);
|
|
40
|
+
const second = rawSecond === undefined ? 0 : Number(rawSecond);
|
|
41
|
+
const offset = match[7];
|
|
42
|
+
const offsetMatch = offset && /^[+-](\d{2}):?(\d{2})$/.exec(offset);
|
|
43
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
44
|
+
const daysInMonth = [
|
|
45
|
+
31,
|
|
46
|
+
leap ? 29 : 28,
|
|
47
|
+
31,
|
|
48
|
+
30,
|
|
49
|
+
31,
|
|
50
|
+
30,
|
|
51
|
+
31,
|
|
52
|
+
31,
|
|
53
|
+
30,
|
|
54
|
+
31,
|
|
55
|
+
30,
|
|
56
|
+
31,
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
month >= 1 &&
|
|
61
|
+
month <= 12 &&
|
|
62
|
+
day >= 1 &&
|
|
63
|
+
day <= daysInMonth[month - 1] &&
|
|
64
|
+
hour <= 23 &&
|
|
65
|
+
minute <= 59 &&
|
|
66
|
+
second <= 59 &&
|
|
67
|
+
(!offsetMatch ||
|
|
68
|
+
(Number(offsetMatch[1]) <= 23 && Number(offsetMatch[2]) <= 59))
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const validateStaticSide = (value, fieldType) =>
|
|
73
|
+
typeof value !== "string" || !value.trim()
|
|
74
|
+
? RESTRICTION_ERRORS.required
|
|
75
|
+
: isValidCalendarDate(value, fieldType === "datetime")
|
|
76
|
+
? null
|
|
77
|
+
: RESTRICTION_ERRORS.invalidDate;
|
|
78
|
+
|
|
79
|
+
const validateDynamicSide = (side) => ({
|
|
80
|
+
amount:
|
|
81
|
+
!side || !isInteger(side.amount) ? RESTRICTION_ERRORS.invalidAmount : null,
|
|
82
|
+
unit:
|
|
83
|
+
!side || !RESTRICTION_UNITS.includes(side.unit)
|
|
84
|
+
? RESTRICTION_ERRORS.invalidUnit
|
|
85
|
+
: null,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const betweenOrderIsInvalid = (between, fieldType) => {
|
|
89
|
+
const { min, max } = resolveRestrictions({ between }, fieldType);
|
|
90
|
+
return Boolean(min && max && min > max);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const validateDateRestrictions = (field) => {
|
|
94
|
+
if (!isDateField(field) || _.isEmpty(field.restrictions)) return {};
|
|
95
|
+
|
|
96
|
+
const restrictions = field.restrictions;
|
|
97
|
+
const between = restrictions.between;
|
|
98
|
+
const legacyBetween =
|
|
99
|
+
!between &&
|
|
100
|
+
(restrictions.between_start !== undefined ||
|
|
101
|
+
restrictions.between_end !== undefined);
|
|
102
|
+
|
|
103
|
+
const restrictionErrors = ["min_date", "max_date"].flatMap((key) => {
|
|
104
|
+
const side = restrictions[key];
|
|
105
|
+
if (!side) return [];
|
|
106
|
+
|
|
107
|
+
if (side.mode === "dynamic") {
|
|
108
|
+
const sideErrors = validateDynamicSide(side);
|
|
109
|
+
return [
|
|
110
|
+
[`${key}.amount`, sideErrors.amount],
|
|
111
|
+
[`${key}.unit`, sideErrors.unit],
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return side.mode === "static"
|
|
116
|
+
? [[`${key}.value`, validateStaticSide(side.value, field.type)]]
|
|
117
|
+
: [["mode", RESTRICTION_ERRORS.invalidMode]];
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const betweenErrors =
|
|
121
|
+
between?.mode === "dynamic"
|
|
122
|
+
? (() => {
|
|
123
|
+
const fromErrors = validateDynamicSide(between.from);
|
|
124
|
+
const toErrors = validateDynamicSide(between.to);
|
|
125
|
+
const hasInvalidSides =
|
|
126
|
+
Object.values(fromErrors).some(Boolean) ||
|
|
127
|
+
Object.values(toErrors).some(Boolean);
|
|
128
|
+
const orderError =
|
|
129
|
+
!hasInvalidSides && betweenOrderIsInvalid(between, field.type)
|
|
130
|
+
? RESTRICTION_ERRORS.invalidOrder
|
|
131
|
+
: null;
|
|
132
|
+
|
|
133
|
+
return [
|
|
134
|
+
["between.from.amount", fromErrors.amount],
|
|
135
|
+
["between.from.unit", fromErrors.unit],
|
|
136
|
+
["between.to.amount", toErrors.amount],
|
|
137
|
+
["between.to.unit", toErrors.unit],
|
|
138
|
+
["between.to.amount", orderError],
|
|
139
|
+
];
|
|
140
|
+
})()
|
|
141
|
+
: between?.mode === "static" || legacyBetween
|
|
142
|
+
? (() => {
|
|
143
|
+
const start = legacyBetween
|
|
144
|
+
? restrictions.between_start
|
|
145
|
+
: between.start;
|
|
146
|
+
const end = legacyBetween ? restrictions.between_end : between.end;
|
|
147
|
+
const startError = validateStaticSide(start, field.type);
|
|
148
|
+
const endError = validateStaticSide(end, field.type);
|
|
149
|
+
const orderError =
|
|
150
|
+
!startError &&
|
|
151
|
+
!endError &&
|
|
152
|
+
betweenOrderIsInvalid({ mode: "static", start, end }, field.type)
|
|
153
|
+
? RESTRICTION_ERRORS.invalidOrder
|
|
154
|
+
: null;
|
|
155
|
+
|
|
156
|
+
return [
|
|
157
|
+
["between.start", startError],
|
|
158
|
+
["between.end", endError || orderError],
|
|
159
|
+
];
|
|
160
|
+
})()
|
|
161
|
+
: between
|
|
162
|
+
? [["mode", RESTRICTION_ERRORS.invalidMode]]
|
|
163
|
+
: [];
|
|
164
|
+
|
|
165
|
+
return Object.fromEntries(
|
|
166
|
+
[...restrictionErrors, ...betweenErrors].filter(([, error]) => error),
|
|
167
|
+
);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export const defaultValueOutOfRange = (field) => {
|
|
171
|
+
if (!isDateField(field)) return false;
|
|
172
|
+
if (_.isEmpty(field.restrictions)) return false;
|
|
173
|
+
|
|
174
|
+
const value = _.pathOr(null, ["default", "value"])(field);
|
|
175
|
+
if (value === null || value === undefined || value === "") return false;
|
|
176
|
+
|
|
177
|
+
const { min, max } = resolveRestrictions(field.restrictions, field.type);
|
|
178
|
+
return checkValuesAgainstBounds(value, { min, max }) !== "ok";
|
|
179
|
+
};
|
|
180
|
+
|
|
5
181
|
const reduceField = (field) => {
|
|
6
182
|
if (field.type === "dynamic_table") {
|
|
7
183
|
const tableColumns = _.defaultTo([])(field?.values?.table_columns);
|
|
@@ -103,6 +279,10 @@ export const parseContentValidation = (content) => {
|
|
|
103
279
|
!_.isEmpty(field.mandatory.on) &&
|
|
104
280
|
_.isEmpty(field.mandatory.to_be);
|
|
105
281
|
|
|
282
|
+
const defaultOutOfRange = defaultValueOutOfRange(field);
|
|
283
|
+
const restrictions = validateDateRestrictions(field);
|
|
284
|
+
const restrictionsInvalid = !_.isEmpty(restrictions);
|
|
285
|
+
|
|
106
286
|
const hasErrors = arrayAnd([
|
|
107
287
|
nameDuplicated,
|
|
108
288
|
nameEmpty,
|
|
@@ -111,10 +291,14 @@ export const parseContentValidation = (content) => {
|
|
|
111
291
|
tableColumnEmpty,
|
|
112
292
|
conditionalVisibilityValueEmpty,
|
|
113
293
|
mandatoryDependingOnValueEmpty,
|
|
294
|
+
defaultOutOfRange,
|
|
295
|
+
restrictionsInvalid,
|
|
114
296
|
]);
|
|
115
297
|
const errors = {
|
|
116
298
|
hasErrors,
|
|
117
299
|
nameDuplicated,
|
|
300
|
+
defaultOutOfRange,
|
|
301
|
+
restrictions,
|
|
118
302
|
};
|
|
119
303
|
|
|
120
304
|
if (field?.type === "dynamic_table") {
|