All files / form/form-fieldset si-form-fieldset.component.ts

100% Statements 28/28
83.33% Branches 5/6
100% Functions 12/12
100% Lines 22/22

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                                                            1x 1x     10x     10x             10x             10x   10x     10x     10x 10x 5x 10x     5x     10x   5x   10x 10x   10x       10x     22x         15x         20x      
/**
 * Copyright (c) Siemens 2016 - 2025
 * SPDX-License-Identifier: MIT
 */
import {
  booleanAttribute,
  ChangeDetectionStrategy,
  Component,
  computed,
  DoCheck,
  HostBinding,
  input,
  signal
} from '@angular/core';
import { SiTranslatePipe, TranslatableString } from '@siemens/element-translate-ng/translate';
 
import { SiFormItemComponent } from '../si-form-item/si-form-item.component';
 
@Component({
  selector: 'si-form-fieldset',
  imports: [SiTranslatePipe],
  templateUrl: './si-form-fieldset.component.html',
  styleUrl: '../si-form.shared.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: {
    role: 'group',
    class: 'si-form-input',
    '[style.--si-form-label-width]': 'labelWidth()'
  }
})
export class SiFormFieldsetComponent implements DoCheck {
  private static labelIdCounter = 0;
 
  /** The label for the entire fieldset. */
  readonly label = input.required<TranslatableString>();
 
  /** Overrides the parent label width. */
  readonly labelWidth = input<string>();
 
  /**
   * Adds a required marker to the label
   *
   * @defaultValue false
   */
  readonly required = input(false, { transform: booleanAttribute });
 
  /**
   * Switches all child inputs to inline mode
   *
   * @defaultValue false
   */
  readonly inline = input(false, { transform: booleanAttribute });
 
  private readonly formItems = signal<SiFormItemComponent[]>([]);
 
  /** @internal */
  readonly hasOnlyRadios = computed(() => {
    // Check if the fieldset only contains radio buttons.
    // We can safely assume that, if all items have the same control name and if there are at least 2 items.
    const items = this.formItems();
    if (items.length > 1) {
      const first = items[0];
      return items.every(item => item.ngControl()?.name === first.ngControl()?.name);
    }
 
    return false;
  });
 
  protected readonly errors = computed(() =>
    // All errors should be the same for radios, so we just take the first.
    this.hasOnlyRadios() ? this.formItems()[0].errors() : []
  );
  protected readonly touched = signal(false);
  protected readonly isRequired = computed(
    () =>
      this.required() || (this.hasOnlyRadios() && this.formItems().every(item => item.required()))
  );
 
  @HostBinding('attr.aria-labelledby')
  protected labelId = `__si-form-fieldset-label-${SiFormFieldsetComponent.labelIdCounter++}`;
 
  ngDoCheck(): void {
    this.touched.set(this.formItems().some(item => item.ngControl()?.touched));
  }
 
  /** @internal */
  registerFormItem(item: SiFormItemComponent): void {
    this.formItems.update(items => [...items, item]);
  }
 
  /** @internal */
  unregisterFormItem(item: SiFormItemComponent): void {
    this.formItems.update(items => items.filter(i => i !== item));
  }
}