Skip to main content

Overview

JoyDoc provides validation for required fields, ensuring users complete necessary information before submitting forms. The validation system respects conditional logic and field visibility.

How It Works

Basic Usage

import { validator } from '@joyfill/components';

// Validate a document
const validation = validator.validate(doc);

Validation Logic

  1. Hidden fields are always valid (respects conditional logic)
  2. Non-required fields are always valid
  3. Required fields with empty values are invalid
  4. Required fields with values are valid

Empty Value Detection

  • Text/Number/Dropdown: Empty if "", null, or undefined
  • Arrays (image, file, multiSelect): Empty if not array, empty array, or all items are empty strings
  • Table : Empty if field.value is an empty array

Configuring Required Fields

Set required: true on any field:
{
  "_id": "name-field",
  "type": "text",
  "title": "Full Name",
  "value": "",
  "required": true,  // Makes field required
  "file": "file1"
}

Validation Results

{
  status: 'valid' | 'invalid' | null,
  fieldValidations: [
    {
      field: { /* field object */ },
      status: 'valid' | 'invalid'
    }
  ]
}

Status meanings:
  • 'valid': All required fields filled
  • 'invalid': One or more required fields empty
  • null: Invalid document structure

Implementation Example

import React, { useEffect, useState } from "react";
import { JoyDoc, validator } from "@joyfill/components";

function JoyDocPage() {
  const [doc, setDoc] = useState(null);
  const [validationResult, setValidationResult] = useState(null);

  useEffect(() => {
    const getDocument = async () => {
      try {
        const response = await fetch("YOUR_API_ENDPOINT", {
          method: "GET",
          headers: {
            Authorization: "Bearer YOUR_TOKEN",
            "Content-Type": "text/JSON",
          },
        });

        const data = await response.json();
        setDoc(data);

        // Validate the document
        if (data) {
          const validation = validator.validate(data);
          setValidationResult(validation);
        }
      } catch (e) {
        console.log(e);
      }
    };

    getDocument();
  }, []);

  return (
    <div>
      {/* Validation Status Display */}
      {validationResult && (
        <div
          style={{
            padding: "20px",
            margin: "20px",
            border: `2px solid ${
              validationResult.status === "valid"
                ? "#4CAF50"
                : validationResult.status === "invalid"
                ? "#f44336"
                : "#ff9800"
            }`,
            borderRadius: "8px",
            backgroundColor:
              validationResult.status === "valid"
                ? "#e8f5e8"
                : validationResult.status === "invalid"
                ? "#ffebee"
                : "#fff3e0",
          }}
        >
          <h3
            style={{
              margin: "0 0 10px 0",
              color:
                validationResult.status === "valid"
                  ? "#2e7d32"
                  : validationResult.status === "invalid"
                  ? "#c62828"
                  : "#ef6c00",
            }}
          >
            Validation Status:{" "}
            {validationResult.status?.toUpperCase() || "NULL"}
          </h3>

          {validationResult.status === "invalid" && (
            <div>
              <p style={{ margin: "10px 0 5px 0", fontWeight: "bold" }}>
                Invalid Fields (
                {
                  validationResult.fieldValidations.filter(
                    (f) => f.status === "invalid"
                  ).length
                }
                ):
              </p>
              <ul style={{ margin: "0", paddingLeft: "20px" }}>
                {validationResult.fieldValidations
                  .filter((fv) => fv.status === "invalid")
                  .map((fieldValidation, index) => (
                    <li
                      key={index}
                      style={{ color: "#c62828", marginBottom: "5px" }}
                    >
                      <strong>
                        {fieldValidation.field?.title ||
                          fieldValidation.field?._id}
                      </strong>
                      <span
                        style={{
                          marginLeft: "10px",
                          fontSize: "12px",
                          color: "#666",
                        }}
                      >
                        ({fieldValidation.field?.type})
                      </span>
                    </li>
                  ))}
              </ul>
            </div>
          )}

          <details style={{ marginTop: "10px" }}>
            <summary style={{ cursor: "pointer", fontWeight: "bold" }}>
              Raw Validation Data
            </summary>
            <pre
              style={{
                marginTop: "10px",
                padding: "10px",
                backgroundColor: "#f5f5f5",
                borderRadius: "4px",
                overflow: "auto",
                fontSize: "12px",
              }}
            >
              {JSON.stringify(validationResult, null, 2)}
            </pre>
          </details>
        </div>
      )}

      <JoyDoc
        doc={doc}
        mode="edit"
        onChange={(params, changes, doc) => {
          // Re-validate when document changes
          if (doc) {
            const validation = validator.validate(doc);
            setValidationResult(validation);
          }
        }}
      />
    </div>
  );
}

export default JoyDocPage;

Key Points

  • Hidden fields are always valid (respects conditional logic)
  • Validate on document changes using the onChange callback
  • Use visual feedback to show validation status to users
  • Debug with raw validation data when troubleshooting