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

Total Symbols
4
Lines of Code
140
Avg Complexity
3.8
Avg Coverage
100.0%

File Relationships

graph LR buildField["buildField"] extractTransferOptions["extractTransferOptions"] buildField -->|calls| extractTransferOptions click buildField "../symbols/34f7e64df4272873.html" click extractTransferOptions "../symbols/46ade5fc61c11f65.html"

Symbols by Kind

method 3
function 1

All Symbols

Name Kind Visibility Status Lines Signature
matches method - 47-52 matches(el: HTMLElement): : boolean
buildField method - 54-71 buildField(wrapper: HTMLElement): : FormField
fill method - 73-119 fill(wrapper: HTMLElement, _value: string): : boolean
extractTransferOptions function - 124-139 extractTransferOptions( wrapper: HTMLElement, ): : Array<{ value: string; text: string }> | undefined

Full Source

/**
 * Ant Design Transfer Adapter
 *
 * Detects and fills `<Transfer>` components.
 *
 * DOM structure (antd v5):
 *   <div class="ant-transfer ...">
 *     <div class="ant-transfer-list">           ← source
 *       <div class="ant-transfer-list-body">
 *         <ul class="ant-transfer-list-content">
 *           <li class="ant-transfer-list-content-item">
 *             <label class="ant-checkbox-wrapper">
 *               <span class="ant-checkbox"><input type="checkbox" /></span>
 *             </label>
 *             <span class="ant-transfer-list-content-item-text">Item</span>
 *           </li>
 *         </ul>
 *       </div>
 *     </div>
 *     <div class="ant-transfer-operation">
 *       <button ... class="ant-btn">></button>
 *       <button ... class="ant-btn"><</button>
 *     </div>
 *     <div class="ant-transfer-list">           ← target
 *       ...
 *     </div>
 *   </div>
 *
 * Filling: Selects items from the source list and clicks the transfer button.
 */

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 antdTransferAdapter: CustomComponentAdapter = {
  name: "antd-transfer",
  selector: ".ant-transfer",

  matches(el: HTMLElement): boolean {
    return (
      el.classList.contains("ant-transfer") &&
      !el.classList.contains("ant-transfer-disabled")
    );
  },

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

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

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

  fill(wrapper: HTMLElement, _value: string): boolean {
    const lists = wrapper.querySelectorAll<HTMLElement>(".ant-transfer-list");
    if (lists.length < 2) return false;

    const sourceList = lists[0];
    const sourceItems = sourceList.querySelectorAll<HTMLElement>(
      ".ant-transfer-list-content-item:not(.ant-transfer-list-content-item-disabled)",
    );

    if (sourceItems.length === 0) return false;

    // Select a random subset of items (at least 1, up to half)
    const count = Math.max(
      1,
      Math.floor(Math.random() * Math.ceil(sourceItems.length / 2)),
    );
    const indices = new Set<number>();
    while (indices.size < count) {
      indices.add(Math.floor(Math.random() * sourceItems.length));
    }

    // Click each selected item's checkbox
    for (const idx of indices) {
      const checkbox = sourceItems[idx].querySelector<HTMLElement>(
        ".ant-checkbox-wrapper:not(.ant-checkbox-wrapper-checked)",
      );
      if (checkbox) {
        simulateClick(checkbox);
      }
    }

    // Click the "move to target" button (first button in operation area)
    const operationArea = wrapper.querySelector<HTMLElement>(
      ".ant-transfer-operation",
    );
    if (operationArea) {
      const moveBtn = operationArea.querySelector<HTMLButtonElement>(
        "button:not([disabled])",
      );
      if (moveBtn) {
        simulateClick(moveBtn);
        return true;
      }
    }

    return false;
  },
};

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

function extractTransferOptions(
  wrapper: HTMLElement,
): Array<{ value: string; text: string }> | undefined {
  const items = wrapper.querySelectorAll<HTMLElement>(
    ".ant-transfer-list:first-child .ant-transfer-list-content-item-text",
  );

  const opts = Array.from(items)
    .map((item) => ({
      value: item.textContent?.trim() ?? "",
      text: item.textContent?.trim() ?? "",
    }))
    .filter((o) => o.text);

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