All files / solutions/modules CategorySelector.ts

0% Statements 0/46
0% Branches 0/30
0% Functions 0/11
0% Lines 0/44

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119                                                                                                                                                                                                                                             
const MIN_CATEGORIES = 1;
const MAX_CATEGORIES = 2;
 
interface CategorySelectorConfig {
  containerId?: string;
  sectionId?: string;
  checkboxSelector?: string;
  min?: number;
  max?: number;
}
 
class CategorySelector {
  container: HTMLElement | null;
  section: HTMLElement | null;
  checkboxSelector: string;
  min: number;
  max: number;
  checkboxes: HTMLInputElement[];
 
  constructor({
    containerId = "categories-list",
    sectionId = "categories-section",
    checkboxSelector = ".category-checkbox",
    min = MIN_CATEGORIES,
    max = MAX_CATEGORIES,
  }: CategorySelectorConfig = {}) {
    this.container = document.getElementById(containerId);
    this.section = document.getElementById(sectionId);
    this.checkboxSelector = checkboxSelector;
    this.min = min;
    this.max = max;
    this.checkboxes = [];
 
    if (!this.container) {
      return;
    }
 
    this.checkboxes = Array.from(
      this.container.querySelectorAll<HTMLInputElement>(checkboxSelector)
    );
 
    this.checkboxes.forEach((checkbox) => {
      checkbox.addEventListener("change", () => this.handleChange());
    });
 
    this.enforce();
  }
 
  getCheckedCount(): number {
    return this.checkboxes.filter((checkbox) => checkbox.checked).length;
  }
 
  handleChange(): void {
    this.enforce();
 
    if (this.getCheckedCount() > 0) {
      this.clearError();
    }
  }
 
  enforce(): void {
    if (!this.checkboxes) {
      return;
    }
 
    const limitReached = this.getCheckedCount() >= this.max;
 
    this.checkboxes.forEach((checkbox) => {
      checkbox.disabled = limitReached && !checkbox.checked;
    });
  }
 
  clearError(): void {
    if (!this.section) {
      return;
    }
 
    this.section.classList.remove("is-error");
 
    const message = this.section.querySelector(".p-form-validation__message");
    if (message) {
      message.setAttribute("hidden", "");
    }
  }
 
  showError(): void {
    if (!this.section) {
      return;
    }
 
    this.section.classList.add("is-error");
 
    const message = this.section.querySelector(".p-form-validation__message");
    if (message) {
      message.removeAttribute("hidden");
    }
 
    this.section.scrollIntoView({ behavior: "smooth" });
  }
 
  isValid(): boolean {
    if (!this.container || this.checkboxes.length === 0) {
      return true;
    }
 
    const count = this.getCheckedCount();
 
    if (count < this.min || count > this.max) {
      this.showError();
      return false;
    }
 
    this.clearError();
    return true;
  }
}
 
export default CategorySelector;