@fogpipe/forma-react 0.9.0 → 0.10.1

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/src/types.ts CHANGED
@@ -300,6 +300,11 @@ export interface FieldWrapperProps {
300
300
  errors: FieldError[];
301
301
  touched: boolean;
302
302
  required: boolean;
303
+ /**
304
+ * Whether to show the required indicator in the UI.
305
+ * False for boolean fields since false is a valid answer.
306
+ */
307
+ showRequiredIndicator: boolean;
303
308
  visible: boolean;
304
309
  }
305
310
 
@@ -422,8 +427,13 @@ export interface GetFieldPropsResult {
422
427
  visible: boolean;
423
428
  /** Whether field is enabled (not disabled) */
424
429
  enabled: boolean;
425
- /** Whether field is required */
430
+ /** Whether field is required (for validation) */
426
431
  required: boolean;
432
+ /**
433
+ * Whether to show the required indicator in the UI.
434
+ * False for boolean fields since false is a valid answer.
435
+ */
436
+ showRequiredIndicator: boolean;
427
437
  /** Whether field has been touched */
428
438
  touched: boolean;
429
439
  /** Validation errors for this field */
package/src/useForma.ts CHANGED
@@ -193,6 +193,23 @@ function formReducer(state: FormState, action: FormAction): FormState {
193
193
  }
194
194
  }
195
195
 
196
+ /**
197
+ * Get default initial values for boolean fields.
198
+ * Boolean fields default to false to avoid undefined state,
199
+ * which provides better UX since false is a valid answer.
200
+ */
201
+ function getDefaultBooleanValues(spec: Forma): Record<string, boolean> {
202
+ const defaults: Record<string, boolean> = {};
203
+ for (const fieldPath of spec.fieldOrder) {
204
+ const schemaProperty = spec.schema.properties?.[fieldPath];
205
+ const fieldDef = spec.fields[fieldPath];
206
+ if (schemaProperty?.type === "boolean" || fieldDef?.type === "boolean") {
207
+ defaults[fieldPath] = false;
208
+ }
209
+ }
210
+ return defaults;
211
+ }
212
+
196
213
  /**
197
214
  * Main Forma hook
198
215
  */
@@ -212,7 +229,7 @@ export function useForma(options: UseFormaOptions): UseFormaReturn {
212
229
  }, [inputSpec, referenceData]);
213
230
 
214
231
  const [state, dispatch] = useReducer(formReducer, {
215
- data: initialData,
232
+ data: { ...getDefaultBooleanValues(spec), ...initialData }, // Boolean defaults merged UNDER initialData
216
233
  touched: {},
217
234
  isSubmitting: false,
218
235
  isSubmitted: false,
@@ -569,6 +586,14 @@ export function useForma(options: UseFormaOptions): UseFormaReturn {
569
586
  const hasErrors = displayedErrors.length > 0;
570
587
  const isRequired = required[path] ?? false;
571
588
 
589
+ // Boolean fields: hide asterisk unless they have validation rules (consent pattern)
590
+ // - Binary question ("Do you smoke?"): no validation → false is valid → hide asterisk
591
+ // - Consent checkbox ("I accept terms"): has validation rule → show asterisk
592
+ const schemaProperty = spec.schema.properties[path];
593
+ const isBooleanField = schemaProperty?.type === "boolean" || fieldDef?.type === "boolean";
594
+ const hasValidationRules = (fieldDef?.validations?.length ?? 0) > 0;
595
+ const showRequiredIndicator = isRequired && (!isBooleanField || hasValidationRules);
596
+
572
597
  return {
573
598
  name: path,
574
599
  value: getValueAtPath(path),
@@ -579,6 +604,7 @@ export function useForma(options: UseFormaOptions): UseFormaReturn {
579
604
  visible: visibility[path] !== false,
580
605
  enabled: enabled[path] !== false,
581
606
  required: isRequired,
607
+ showRequiredIndicator,
582
608
  touched: isTouched,
583
609
  errors: displayedErrors,
584
610
  onChange: handlers.onChange,
@@ -634,6 +660,7 @@ export function useForma(options: UseFormaOptions): UseFormaReturn {
634
660
  visible: true,
635
661
  enabled: enabled[path] !== false,
636
662
  required: false, // TODO: Evaluate item field required
663
+ showRequiredIndicator: false, // Item fields don't show required indicator
637
664
  touched: isTouched,
638
665
  errors: showErrors ? fieldErrors : [],
639
666
  onChange: handlers.onChange,