src/lib/form/adapters/antd/antd-checkbox-adapter.ts

Total Symbols
4
Lines of Code
115
Avg Complexity
3.5
Avg Coverage
100.0%

File Relationships

graph LR buildField["buildField"] extractCheckboxOptions["extractCheckboxOptions"] buildField -->|calls| extractCheckboxOptions click buildField "../symbols/2a40f31d63f1a0b7.html" click extractCheckboxOptions "../symbols/c3e26b44e414ea6a.html"

Symbols by Kind

method 3
function 1

All Symbols

Name Kind Visibility Status Lines Signature
matches method - 33-35 matches(el: HTMLElement): : boolean
buildField method - 37-54 buildField(wrapper: HTMLElement): : FormField
fill method - 56-90 fill(wrapper: HTMLElement, value: string): : boolean
extractCheckboxOptions function - 95-114 extractCheckboxOptions( wrapper: HTMLElement, ): : Array<{ value: string; text: string }> | undefined

Full Source

/**
 * Ant Design Checkbox Group Adapter
 *
 * Detects and fills `<Checkbox.Group>` components.
 *
 * DOM structure (antd v5):
 *   <div class="ant-checkbox-group">
 *     <label class="ant-checkbox-wrapper">
 *       <span class="ant-checkbox"><input type="checkbox" /></span>
 *       <span>Option text</span>
 *     </label>
 *     ...
 *   </div>
 *
 * Filling: Clicks a random unchecked checkbox (or matches by value/text).
 */

import type { FormField } from "@/types";
import type { CustomComponentAdapter } from "../adapter.interface";
import {
  findAntLabel,
  findAntId,
  isAntRequired,
  simulateClick,
  getAntdSelector,
} from "./antd-utils";
import { buildSignals } from "../../extractors";

export const antdCheckboxAdapter: CustomComponentAdapter = {
  name: "antd-checkbox",
  selector: ".ant-checkbox-group",

  matches(el: HTMLElement): boolean {
    return el.classList.contains("ant-checkbox-group");
  },

  buildField(wrapper: HTMLElement): FormField {
    const options = extractCheckboxOptions(wrapper);

    const field: FormField = {
      element: wrapper,
      selector: getAntdSelector(wrapper),
      category: "unknown",
      fieldType: "checkbox",
      adapterName: "antd-checkbox",
      label: findAntLabel(wrapper),
      id: findAntId(wrapper),
      required: isAntRequired(wrapper),
      options,
    };

    field.contextSignals = buildSignals(field);
    return field;
  },

  fill(wrapper: HTMLElement, value: string): boolean {
    const labels = wrapper.querySelectorAll<HTMLElement>(
      ".ant-checkbox-wrapper",
    );

    // Try matching by value or text
    for (const label of labels) {
      const input = label.querySelector<HTMLInputElement>(
        "input[type='checkbox']",
      );
      const text = label.textContent?.trim() ?? "";

      if (
        input?.value === value ||
        text.toLowerCase() === value.toLowerCase() ||
        text.toLowerCase().includes(value.toLowerCase())
      ) {
        if (!label.classList.contains("ant-checkbox-wrapper-checked")) {
          simulateClick(label);
        }
        return true;
      }
    }

    // Fallback: check the first unchecked checkbox
    const unchecked = wrapper.querySelector<HTMLElement>(
      ".ant-checkbox-wrapper:not(.ant-checkbox-wrapper-checked)",
    );
    if (unchecked) {
      simulateClick(unchecked);
      return true;
    }

    return false;
  },
};

// ── Helpers ───────────────────────────────────────────────────────────────────

function extractCheckboxOptions(
  wrapper: HTMLElement,
): Array<{ value: string; text: string }> | undefined {
  const labels = wrapper.querySelectorAll<HTMLElement>(".ant-checkbox-wrapper");

  const opts = Array.from(labels)
    .map((label) => {
      const input = label.querySelector<HTMLInputElement>(
        "input[type='checkbox']",
      );
      const text = label.textContent?.trim() ?? "";
      return {
        value: input?.value || text,
        text,
      };
    })
    .filter((o) => o.text);

  return opts.length > 0 ? opts : undefined;
}