@openg2p/registry-widgets 1.1.0-dev.8 → 1.1.0-dev.9
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/components/SectionRenderer.d.ts.map +1 -1
- package/dist/components/SectionsContainer.d.ts.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.esm.js +52 -18
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +52 -18
- package/dist/index.js.map +1 -1
- package/dist/utils/sectionValidate.d.ts +9 -1
- package/dist/utils/sectionValidate.d.ts.map +1 -1
- package/dist/utils/validation.d.ts +6 -2
- package/dist/utils/validation.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -246,24 +246,27 @@ const getValidationPattern = (validationType) => {
|
|
|
246
246
|
};
|
|
247
247
|
|
|
248
248
|
/**
|
|
249
|
-
* Validate value against validation rules
|
|
249
|
+
* Validate value against validation rules.
|
|
250
|
+
*
|
|
251
|
+
* @param skipRequired - When true, required-field checks are skipped (used by
|
|
252
|
+
* per-section Save/Next buttons so the user can move between sections without
|
|
253
|
+
* filling every mandatory field; only format/range checks still run).
|
|
250
254
|
*/
|
|
251
|
-
const validateWidget = (value, validation, required = false) => {
|
|
255
|
+
const validateWidget = (value, validation, required = false, skipRequired = false) => {
|
|
252
256
|
const errors = [];
|
|
253
257
|
if (!validation && !required) {
|
|
254
258
|
return errors;
|
|
255
259
|
}
|
|
256
|
-
// Check required
|
|
257
|
-
const isRequired = validation?.required ?? required;
|
|
260
|
+
// Check required (skipped when navigating between sections)
|
|
261
|
+
const isRequired = !skipRequired && (validation?.required ?? required);
|
|
258
262
|
// For boolean, false is a valid value, so only check for null/undefined/empty string
|
|
259
263
|
const isEmpty = value === null || value === undefined || value === '';
|
|
260
264
|
if (isRequired && isEmpty) {
|
|
261
265
|
errors.push('This field is required');
|
|
262
266
|
return errors; // Return early if required field is empty
|
|
263
267
|
}
|
|
264
|
-
// Skip
|
|
265
|
-
|
|
266
|
-
if (isEmpty && !isRequired) {
|
|
268
|
+
// Skip format/range validations if value is empty
|
|
269
|
+
if (isEmpty) {
|
|
267
270
|
return errors;
|
|
268
271
|
}
|
|
269
272
|
if (!validation) {
|
|
@@ -3807,7 +3810,15 @@ const collectWidgets = (panels) => {
|
|
|
3807
3810
|
});
|
|
3808
3811
|
return widgets;
|
|
3809
3812
|
};
|
|
3810
|
-
|
|
3813
|
+
/**
|
|
3814
|
+
* Validate all widgets in a section and dispatch errors to the store.
|
|
3815
|
+
*
|
|
3816
|
+
* @param skipRequired - When true, required-field checks (widget-required,
|
|
3817
|
+
* validation.required, document-required) are skipped. Use this for
|
|
3818
|
+
* per-section Save/Next navigation so the user can advance without filling
|
|
3819
|
+
* every mandatory field; only format/range errors are reported.
|
|
3820
|
+
*/
|
|
3821
|
+
const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = false) => {
|
|
3811
3822
|
const allWidgets = collectWidgets(section.panels);
|
|
3812
3823
|
let isValid = true;
|
|
3813
3824
|
for (const widget of allWidgets) {
|
|
@@ -3816,7 +3827,7 @@ const sectionValidate = (section, currentSchemaData, dispatch) => {
|
|
|
3816
3827
|
continue;
|
|
3817
3828
|
const widgetId = widget['widget-id'];
|
|
3818
3829
|
const value = getWidgetValue(currentSchemaData, widget['widget-data-path'], widgetId);
|
|
3819
|
-
const errors = validateWidget(value, widget['widget-data-validation'], widget['widget-required']);
|
|
3830
|
+
const errors = validateWidget(value, widget['widget-data-validation'], widget['widget-required'], skipRequired);
|
|
3820
3831
|
if (errors.length > 0) {
|
|
3821
3832
|
isValid = false;
|
|
3822
3833
|
dispatch(setTouched({ widgetId, touched: true }));
|
|
@@ -3827,10 +3838,10 @@ const sectionValidate = (section, currentSchemaData, dispatch) => {
|
|
|
3827
3838
|
dispatch(setError({ widgetId, errors: [] }));
|
|
3828
3839
|
}
|
|
3829
3840
|
}
|
|
3830
|
-
// Supporting Documents
|
|
3841
|
+
// Supporting Documents — only check required when not skipping required validation
|
|
3831
3842
|
section['section-supporting-documents']?.forEach((doc, index) => {
|
|
3832
3843
|
const widgetId = `supporting-doc-${section['section-id']}-${index}`;
|
|
3833
|
-
if (doc['document-required']) {
|
|
3844
|
+
if (!skipRequired && doc['document-required']) {
|
|
3834
3845
|
const file = getValueByPath(currentSchemaData, doc['document-data-path']);
|
|
3835
3846
|
if (!file) {
|
|
3836
3847
|
isValid = false;
|
|
@@ -4465,7 +4476,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4465
4476
|
const sectionWidgets = collectWidgets(originalSection.panels);
|
|
4466
4477
|
const currentState = store.getState().widget;
|
|
4467
4478
|
const currentSchemaData = currentState.values || {};
|
|
4468
|
-
const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch);
|
|
4479
|
+
const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch, true);
|
|
4469
4480
|
if (!isSectionValid) {
|
|
4470
4481
|
return;
|
|
4471
4482
|
}
|
|
@@ -4531,7 +4542,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4531
4542
|
const sectionWidgets = collectWidgets(originalSection.panels);
|
|
4532
4543
|
const currentState = store.getState().widget;
|
|
4533
4544
|
const currentSchemaData = currentState.values || {};
|
|
4534
|
-
const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch);
|
|
4545
|
+
const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch, true);
|
|
4535
4546
|
if (!isSectionValid)
|
|
4536
4547
|
return;
|
|
4537
4548
|
const oldSchemaData = schemaData || contextSchemaData;
|
|
@@ -5165,6 +5176,10 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
|
|
|
5165
5176
|
// Stable ref for namespace so formHandle useMemo doesn't depend on the (possibly inline) function identity
|
|
5166
5177
|
const namespaceRef = React.useRef(namespace);
|
|
5167
5178
|
namespaceRef.current = namespace;
|
|
5179
|
+
// Stable refs so formHandle closure can access current mode and accordion setter without stale captures
|
|
5180
|
+
const modeRef = React.useRef(mode);
|
|
5181
|
+
modeRef.current = mode;
|
|
5182
|
+
const setExpandedSectionIndexRef = React.useRef(setExpandedSectionIndex);
|
|
5168
5183
|
// Track dirty (unsaved changes) per section for form handle validation
|
|
5169
5184
|
const sectionDirtyMapRef = React.useRef({});
|
|
5170
5185
|
const handleSectionDirtyChange = React.useCallback((sectionId, isDirty) => {
|
|
@@ -5226,33 +5241,52 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
|
|
|
5226
5241
|
};
|
|
5227
5242
|
return {
|
|
5228
5243
|
validate: async () => {
|
|
5229
|
-
|
|
5244
|
+
if (modeRef.current !== 'IntakeForm')
|
|
5245
|
+
checkNoUnsavedChanges();
|
|
5230
5246
|
const values = getValues();
|
|
5231
5247
|
let allValid = true;
|
|
5248
|
+
let firstInvalidIndex = null;
|
|
5232
5249
|
for (let i = 0; i < safeSections.length; i++) {
|
|
5233
5250
|
const section = safeSections[i];
|
|
5234
5251
|
const ns = getNamespace(section, i);
|
|
5235
5252
|
const sectionToValidate = ns ? namespaceSectionConfig(section, ns) : section;
|
|
5236
5253
|
const valid = sectionValidate(sectionToValidate, values, dispatch);
|
|
5237
|
-
if (!valid)
|
|
5254
|
+
if (!valid) {
|
|
5255
|
+
if (firstInvalidIndex === null)
|
|
5256
|
+
firstInvalidIndex = i;
|
|
5238
5257
|
allValid = false;
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
5260
|
+
if (!allValid && modeRef.current === 'IntakeForm' && firstInvalidIndex !== null) {
|
|
5261
|
+
setExpandedSectionIndexRef.current(firstInvalidIndex);
|
|
5239
5262
|
}
|
|
5240
5263
|
return allValid;
|
|
5241
5264
|
},
|
|
5242
5265
|
getFormData: () => getValues(),
|
|
5243
5266
|
validateAndGetData: async () => {
|
|
5244
|
-
|
|
5267
|
+
if (modeRef.current !== 'IntakeForm')
|
|
5268
|
+
checkNoUnsavedChanges();
|
|
5245
5269
|
const values = getValues();
|
|
5246
5270
|
const results = [];
|
|
5271
|
+
let firstInvalidIndex = null;
|
|
5247
5272
|
for (let i = 0; i < safeSections.length; i++) {
|
|
5248
5273
|
const section = safeSections[i];
|
|
5249
5274
|
const ns = getNamespace(section, i);
|
|
5250
5275
|
const sectionToValidate = ns ? namespaceSectionConfig(section, ns) : section;
|
|
5251
5276
|
const valid = sectionValidate(sectionToValidate, values, dispatch);
|
|
5252
5277
|
if (!valid) {
|
|
5253
|
-
|
|
5278
|
+
if (firstInvalidIndex === null)
|
|
5279
|
+
firstInvalidIndex = i;
|
|
5254
5280
|
}
|
|
5255
|
-
|
|
5281
|
+
else {
|
|
5282
|
+
results.push(buildSectionChanges(section, values, ns));
|
|
5283
|
+
}
|
|
5284
|
+
}
|
|
5285
|
+
if (firstInvalidIndex !== null) {
|
|
5286
|
+
if (modeRef.current === 'IntakeForm') {
|
|
5287
|
+
setExpandedSectionIndexRef.current(firstInvalidIndex);
|
|
5288
|
+
}
|
|
5289
|
+
throw new Error('Validation failed. Please fix the errors and try again.');
|
|
5256
5290
|
}
|
|
5257
5291
|
return results;
|
|
5258
5292
|
},
|