npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

button-toggle-input

v15.0.4

Published

This is an Angular Module containing Components/Services using Material

Readme

Button Toggle Input Component

Overview

The button-toggle-input library provides a comprehensive Material Design toggle button component that works seamlessly with Angular forms. It supports both single and multiple selection modes, customizable styling, tooltips, and various display options including icons, labels, and borders. The component implements Angular's ControlValueAccessor interface for seamless form integration.

Core Capabilities

🎯 Advanced Toggle Button Interface

  • Single/Multiple Selection: Support for both single selection and multi-select modes
  • Rich Display Options: Icons, labels, tooltips, and custom styling
  • Form Control Integration: Native Angular form control support with ControlValueAccessor
  • Material Design: Built on Angular Material button toggle foundation
  • Customizable Styling: Colors, borders, width, and icon positioning
  • Tooltip Support: Contextual tooltips with configurable positioning
  • Accessibility Ready: ARIA labels and keyboard navigation support

🔧 Features

ControlValueAccessor Implementation - Works with Angular forms
Material Design Integration - Uses Angular Material components
Single & Multiple Selection - Flexible selection modes
Icon Support - Material icons with prefix/suffix positioning
Tooltip Integration - Contextual help with positioning options
Custom Color Schemes - Light/dark colors and border control
Full Width Option - Responsive width control
Change Detection Optimized - OnPush change detection strategy
Form Validation Support - Integrates with Angular validation

Key Benefits

| Feature | Description | |---------|-------------| | Flexible Selection | Support for single or multiple item selection | | Rich UI Options | Icons, labels, tooltips, and styling customization | | Form Integration | Seamless Angular form control integration | | Material Design | Consistent Material Design styling and behavior | | Performance Optimized | OnPush change detection for better performance | | Accessibility | Full ARIA support and keyboard navigation |


Demo Component (ButtonToggleDemoComponent)

The demo component showcases various toggle button configurations and demonstrates different use cases including icons-only, labels-only, and combined icon+label modes.

Usage

To use the demo component in your application:

<app-button-toggle-demo></app-button-toggle-demo>

The demo includes:

  • Icon-only mode: Toggle buttons with Material icons
  • Label-only mode: Toggle buttons with text labels
  • Combined mode: Toggle buttons with both icons and labels
  • Form controls: Reactive form integration examples
  • Dynamic data switching: Runtime data type changes
  • Enable/disable controls: Form control state management
  • Change detection logging: Console output for debugging

Summary

The button-toggle-input library provides a flexible, Material Design-compliant toggle button component with comprehensive form integration, customizable styling, and rich display options for Angular applications.


Quick Start Guide

Installation & Setup (2 minutes)

1. Import Module

// app.module.ts
import { ButtonToggleInputModule } from 'button-toggle-input';

@NgModule({
  imports: [
    ButtonToggleInputModule
  ]
})
export class AppModule { }

2. No Module Configuration Required

The ButtonToggleInputModule does not require global configuration. Components can be used immediately after module import.

Quick Examples

Example 1: Basic Single Selection

import { Component } from '@angular/core';
import { ListItem } from 'button-toggle-input';

@Component({
  selector: 'app-basic-toggle',
  template: `
    <app-button-toggle-input
      [formControl]="selectionControl"
      [data]="toggleOptions">
    </app-button-toggle-input>
    
    <div>Selected: {{ selectionControl.value }}</div>
  `
})
export class BasicToggleComponent {
  selectionControl = new FormControl();
  
  toggleOptions: ListItem[] = [
    { value: 'option1', label: 'Option 1', icon: 'home' },
    { value: 'option2', label: 'Option 2', icon: 'settings' },
    { value: 'option3', label: 'Option 3', icon: 'star' }
  ];
}

Example 2: Multiple Selection with Tooltips

import { Component } from '@angular/core';
import { ListItem } from 'button-toggle-input';

@Component({
  selector: 'app-multi-toggle',
  template: `
    <app-button-toggle-input
      [formControl]="multiControl"
      [data]="multiOptions"
      [multiple]="true"
      [toolTips]="true"
      toolTipPosition="below"
      [noBorder]="true"
      [fullWidth]="true">
    </app-button-toggle-input>
    
    <div>Selected Items: {{ multiControl.value | json }}</div>
  `
})
export class MultiToggleComponent {
  multiControl = new FormControl();
  
  multiOptions: ListItem[] = [
    { 
      value: 'read', 
      label: 'Read', 
      icon: 'visibility',
      tootTip: 'Read access to documents'
    },
    { 
      value: 'write', 
      label: 'Write', 
      icon: 'edit',
      tootTip: 'Write access to documents'
    },
    { 
      value: 'delete', 
      label: 'Delete', 
      icon: 'delete',
      tootTip: 'Delete access to documents'
    }
  ];
}

Example 3: Icon-Only Mode

import { Component } from '@angular/core';
import { ListItem } from 'button-toggle-input';

@Component({
  selector: 'app-icon-toggle',
  template: `
    <app-button-toggle-input
      [formControl]="iconControl"
      [data]="iconOptions"
      [iconPrefix]="true"
      [noBorder]="false">
    </app-button-toggle-input>
    
    <div>Selected: {{ iconControl.value }}</div>
  `
})
export class IconToggleComponent {
  iconControl = new FormControl();
  
  iconOptions: ListItem[] = [
    { value: 'grid', icon: 'grid_on', tootTip: 'Grid View' },
    { value: 'list', icon: 'list', tootTip: 'List View' },
    { value: 'chart', icon: 'bar_chart', tootTip: 'Chart View' }
  ];
}

Example 4: Custom Styled Toggle

import { Component } from '@angular/core';
import { ListItem } from 'button-toggle-input';

@Component({
  selector: 'app-styled-toggle',
  template: `
    <div class="toggle-container">
      <h3>View Options</h3>
      
      <app-button-toggle-input
        [formControl]="viewControl"
        [data]="viewOptions"
        color="#007bff"
        lightColor="#f0f8ff"
        darkColor="white"
        [fullWidth]="true">
      </app-button-toggle-input>
      
      <div class="result">
        Current view: {{ viewControl.value }}
      </div>
    </div>
  `,
  styles: [`
    .toggle-container {
      max-width: 400px;
      margin: 2rem auto;
      padding: 1rem;
      border: 1px solid #ddd;
      border-radius: 8px;
    }
    h3 {
      text-align: center;
      margin-bottom: 1rem;
      color: #333;
    }
    .result {
      margin-top: 1rem;
      text-align: center;
      font-weight: bold;
      color: #007bff;
    }
  `]
})
export class StyledToggleComponent {
  viewControl = new FormControl();
  
  viewOptions: ListItem[] = [
    { value: 'compact', label: 'Compact', icon: 'compress' },
    { value: 'comfortable', label: 'Comfortable', icon: 'aspect_ratio' },
    { value: 'spacious', label: 'Spacious', icon: 'open_in_full' }
  ];
}

Example 5: Dynamic Data with Validation

import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { ListItem } from 'button-toggle-input';

@Component({
  selector: 'app-dynamic-toggle',
  template: `
    <form [formGroup]="toggleForm">
      <app-button-toggle-input
        formControlName="category"
        [data]="categoryOptions"
        [multiple]="false"
        [toolTips]="true">
      </app-button-toggle-input>
      
      <app-button-toggle-input
        formControlName="features"
        [data]="featureOptions"
        [multiple]="true"
        [toolTips]="true">
      </app-button-toggle-input>
    </form>
    
    <div class="form-status">
      <div>Category Valid: {{ toggleForm.get('category')?.valid }}</div>
      <div>Features Valid: {{ toggleForm.get('features')?.valid }}</div>
      <div>Form Valid: {{ toggleForm.valid }}</div>
    </div>
  `,
  styles: [`
    form {
      display: flex;
      flex-direction: column;
      gap: 2rem;
      max-width: 500px;
      margin: 2rem auto;
    }
    .form-status {
      background: #f5f5f5;
      padding: 1rem;
      border-radius: 4px;
      font-family: monospace;
    }
  `]
})
export class DynamicToggleComponent {
  constructor(private fb: FormBuilder) {}
  
  toggleForm = this.fb.group({
    category: ['', Validators.required],
    features: [[], Validators.minLength(1)]
  });
  
  categoryOptions: ListItem[] = [
    { value: 'web', label: 'Web Application', icon: 'web' },
    { value: 'mobile', label: 'Mobile App', icon: 'phone_android' },
    { value: 'desktop', label: 'Desktop App', icon: 'computer' }
  ];
  
  featureOptions: ListItem[] = [
    { value: 'auth', label: 'Authentication', icon: 'security' },
    { value: 'notifications', label: 'Notifications', icon: 'notifications' },
    { value: 'offline', label: 'Offline Support', icon: 'wifi_off' },
    { value: 'analytics', label: 'Analytics', icon: 'analytics' }
  ];
}

Component API

Inputs

| Input | Type | Description | Default | | :--- | :--- | :--- | :--- | | data | ListItem[] \| string[] | Array of ListItem objects or strings defining toggle options | (Required) | | multiple | boolean | If true, allows multiple selections | false | | toolTips | boolean | If true, displays tooltips on each toggle item | false | | toolTipPosition | TooltipPosition | Position of tooltips ('above', 'below', 'left', 'right') | "above" | | toolTipShowDelay | number | Delay in milliseconds before tooltip is displayed | 1 | | color | string | Base color of toggle buttons (hex code or CSS color) | "#333333" | | lightColor | string | Color for light areas (background when not selected) | "white" | | darkColor | string | Color for dark areas (text when selected) | "black" | | noBorder | boolean | If true, removes border from toggle buttons | false | | iconPrefix | boolean | If true, displays icon before label (ignored if iconSuffix is true) | true | | iconSuffix | boolean | If true, displays icon after label (sets iconPrefix to false) | false | | fullWidth | boolean | If true, makes toggle buttons take full container width | false |

Outputs

| Output | Type | Description | | :--- | :--- | :--- | | (Inherited from ControlValueAccessor) | Function | Value changes emitted through Angular's form control |


Model Structures

ListItem Interface

export interface ListItemInterface {
  value: any;           // The value returned when this item is selected
  label?: string;       // Optional display label text
  icon?: string;        // Optional Material icon name
  tootTip?: string;     // Optional tooltip text (note: typo in original)
  selected?: string;    // Internal: marks item as selected
  disabled?: string;    // Internal: marks item as disabled
}

ListItem Class

export class ListItem implements ListItemInterface {
  constructor(
    public value = '',
    public label?: string,
    public icon?: string,
    public tootTip?: string,
    public selected?: string,
    public disabled?: string,
  ) {}

  static adapt(item?: any): ListItem {
    return new ListItem(
      item?.value,
      // Label: use provided label, or empty string if icon exists, or fallback to value
      (item?.label) ? item.label : (item?.icon) ? '' : item?.value,
      item?.icon,
      // Tooltip: use provided tooltip, or label if exists, or fallback to value
      (item?.tootTip) ? item.tootTip : (item?.label) ? item.label : item?.value,
      item?.selected,
      item?.disabled,
    );
  }
}

Usage Examples

// Basic list item
const basicItem = new ListItem('value1', 'Label 1');

// With icon
const iconItem = new ListItem('value2', 'Settings', 'settings');

// Using adapt method for flexible data
const adaptedItem = ListItem.adapt({
  value: 'option1',
  label: 'Option 1',
  icon: 'radio_button_checked',
  tootTip: 'Select this option'
});

// Array configurations
const toggleOptions: ListItem[] = [
  // String-only data (automatically converted)
  'option1',
  'option2',
  'option3',
  
  // Object configuration
  { value: 'custom1', label: 'Custom Option', icon: 'star' },
  { value: 'custom2', label: 'Another Option', icon: 'heart' },
  
  // Icon-only (no label)
  { value: 'icon1', icon: 'home', tootTip: 'Home View' },
  { value: 'icon2', icon: 'work', tootTip: 'Work View' }
];

// Complex configuration with all properties
const complexItem: ListItem = ListItem.adapt({
  value: 'complex-option',
  label: 'Complex Option',
  icon: 'settings',
  tootTip: 'This is a complex option with all features',
  selected: 'true',    // Pre-selected
  disabled: 'false'    // Not disabled
});

Form Integration (ControlValueAccessor)

The component implements Angular's ControlValueAccessor interface, making it fully compatible with both template-driven and reactive forms.

ControlValueAccessor Implementation

Methods Implemented

// writeValue(value: any): void
// Sets the value of the control
writeValue(value: any): void {
  // Handle incoming form control value
  // Update component state accordingly
}

// registerOnChange(fn: any): void
// Registers a callback for value changes
registerOnChange(fn: any): void {
  this.onChange = fn;
}

// registerOnTouched(fn: any): void
// Registers a callback for touch events
registerOnTouched(fn: any): void {
  this.onTouch = fn;
}

// setDisabledState(isDisabled: boolean): void
// Sets disabled state
setDisabledState?(isDisabled: boolean): void {
  this.disabled = isDisabled;
}

Form Integration Examples

Reactive Forms

import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-reactive-form-toggle',
  template: `
    <form [formGroup]="toggleForm">
      <app-button-toggle-input
        formControlName="selection"
        [data]="options"
        [multiple]="true">
      </app-button-toggle-input>
      
      <div class="form-errors" *ngIf="toggleForm.get('selection')?.errors">
        <div *ngIf="toggleForm.get('selection')?.hasError('required')">
          Selection is required
        </div>
        <div *ngIf="toggleForm.get('selection')?.hasError('minLength')">
          At least one option must be selected
        </div>
      </div>
    </form>
  `
})
export class ReactiveFormToggleComponent {
  toggleForm = new FormGroup({
    selection: new FormControl([], Validators.required)
  });
  
  options: ListItem[] = [
    { value: 'opt1', label: 'Option 1' },
    { value: 'opt2', label: 'Option 2' },
    { value: 'opt3', label: 'Option 3' }
  ];
}

Template-Driven Forms

import { Component } from '@angular/core';

@Component({
  selector: 'app-template-toggle',
  template: `
    <form #toggleForm="ngForm">
      <app-button-toggle-input
        [(ngModel)]="selectedValue"
        name="toggleSelection"
        [data]="options"
        [multiple]="false"
        required>
      </app-button-toggle-input>
      
      <div *ngIf="toggleForm.controls.toggleSelection?.invalid && toggleForm.controls.toggleSelection?.touched">
        Selection is required
      </div>
    </form>
  `
})
export class TemplateToggleComponent {
  selectedValue: any = null;
  
  options: ListItem[] = [
    { value: 'single1', label: 'Single Option 1' },
    { value: 'single2', label: 'Single Option 2' },
    { value: 'single3', label: 'Single Option 3' }
  ];
}

Form Control Methods

// Setting values programmatically
this.formControl.setValue(['option1', 'option2']); // For multiple mode
this.formControl.setValue('option1');              // For single mode

// Patching values
this.formControl.patchValue(['option1']);          // Partial update

// Resetting
this.formControl.reset();                          // Clear selection

// Getting current value
const currentValue = this.formControl.value;

// Checking validity
const isValid = this.formControl.valid;
const errors = this.formControl.errors;

// Setting custom errors
this.formControl.setErrors({ customError: 'Custom error message' });

Module Configuration

ButtonToggleInputModule

No Global Configuration Required

The ButtonToggleInputModule does not provide a forRoot() method or global configuration options. All configuration is done at the component level through input properties.

Module Structure

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    MatIconModule,
    MatButtonModule,
    MatTooltipModule,
    ReactiveFormsModule,
    MatButtonToggleModule,
    MatSlideToggleModule,
    MatDividerModule,
  ],
  declarations: [
    ButtonToggleInputComponent,
    ButtonToggleDemoComponent
  ],
  exports: [
    ButtonToggleInputComponent,
    ButtonToggleDemoComponent
  ]
})
export class ButtonToggleInputModule { }

Dependencies

  • @angular/common: Core Angular functionality
  • @angular/forms: Form control integration (FormsModule, ReactiveFormsModule)
  • @angular/material: Material Design components
    • MatIconModule: Icon display
    • MatButtonModule: Button base components
    • MatTooltipModule: Tooltip functionality
    • MatButtonToggleModule: Button toggle foundation
    • MatSlideToggleModule: Slide toggle components
    • MatDividerModule: Visual divider components

Styling and Customization

CSS Classes and Styling

The component uses Material Design styling and can be customized using:

  1. Global Material Theme: Configure colors in your Angular Material theme
  2. Component-specific Styles: Add custom CSS classes
  3. Input Properties: Use color, lightColor, darkColor, and noBorder properties
  4. Container Styling: Style the parent container for layout control

Color Customization

// Custom color schemes
.light-theme {
  app-button-toggle-input {
    --toggle-color: #2196f3;
    --toggle-light-color: #e3f2fd;
    --toggle-dark-color: #ffffff;
  }
}

.dark-theme {
  app-button-toggle-input {
    --toggle-color: #64b5f6;
    --toggle-light-color: #1565c0;
    --toggle-dark-color: #ffffff;
  }
}

Layout Customization

// Full width toggle
.full-width-toggle {
  app-button-toggle-input {
    width: 100%;
    
    .mat-button-toggle-group {
      width: 100%;
      
      .mat-button-toggle {
        flex: 1;
      }
    }
  }
}

// Compact toggle
.compact-toggle {
  app-button-toggle-input {
    .mat-button-toggle {
      padding: 4px 8px;
      min-width: auto;
    }
  }
}

Border Customization

// Remove borders
.no-borders {
  app-button-toggle-input {
    .mat-button-toggle {
      border: none;
      
      &.mat-button-toggle-checked {
        border: none;
      }
    }
  }
}

// Custom border styles
.custom-borders {
  app-button-toggle-input {
    .mat-button-toggle {
      border: 2px solid #e0e0e0;
      border-radius: 8px;
      margin: 2px;
      
      &.mat-button-toggle-checked {
        border-color: #2196f3;
        background-color: #e3f2fd;
      }
    }
  }
}

Accessibility

ARIA Support

  • Buttons include proper ARIA labels and roles
  • Keyboard navigation is fully supported (Tab, Enter, Space, Arrow keys)
  • Screen reader friendly with appropriate labels and descriptions
  • Tooltip content is accessible to assistive technologies
  • Selection state is properly communicated

Best Practices

  1. Provide meaningful labels for all toggle items
  2. Use descriptive icons that match the toggle purpose
  3. Set appropriate tooltips for additional context
  4. Consider color contrast when using custom colors
  5. Test with screen readers to ensure accessibility
  6. Use logical tab order for keyboard navigation

Keyboard Navigation

| Key | Action | |-----|--------| | Tab | Navigate to next toggle item | | Shift+Tab | Navigate to previous toggle item | | Space | Toggle selection (single mode) | | Enter | Toggle selection (single mode) | | Arrow Keys | Navigate between toggle items | | Ctrl+Click | Toggle selection (multiple mode) |


Integration Examples

With Other UI Components

// Integration with display-card
@Component({
  template: `
    <app-display-card title="Filter Options">
      <app-button-toggle-input
        [data]="filterOptions"
        [multiple]="true"
        [toolTips]="true"
        [fullWidth]="true">
      </app-button-toggle-input>
    </app-display-card>
  `
})
export class CardWithToggleComponent {
  filterOptions: ListItem[] = [
    { value: 'active', label: 'Active', icon: 'check_circle' },
    { value: 'pending', label: 'Pending', icon: 'schedule' },
    { value: 'archived', label: 'Archived', icon: 'archive' }
  ];
}

With State Management

// Integration with HTTP Request Manager
@Component({
  template: `
    <app-button-toggle-input
      [data]="viewOptions$ | async"
      [multiple]="false"
      (selectionChange)="handleViewChange($event)">
    </app-button-toggle-input>
  `
})
export class StateManagedToggleComponent {
  viewOptions$ = this.viewStore.options$;
  
  constructor(private viewStore: ViewStore) {}
  
  handleViewChange(value: any) {
    this.viewStore.setView(value);
  }
}

With Dynamic Forms

@Component({
  template: `
    <div formArrayName="toggles">
      <div *ngFor="let toggle of toggleArray.controls; let i = index">
        <app-button-toggle-input
          [formControlName]="i"
          [data]="dynamicOptions[i]">
        </app-button-toggle-input>
      </div>
    </div>
  `
})
export class DynamicFormComponent {
  toggleForm = this.fb.group({
    toggles: this.fb.array([
      this.fb.control('option1'),
      this.fb.control(['option1', 'option2']),
      this.fb.control('option3')
    ])
  });
  
  get toggleArray() {
    return this.toggleForm.get('toggles') as FormArray;
  }
  
  dynamicOptions: ListItem[][] = [
    // Options for first toggle
    [{ value: 'opt1', label: 'Option 1' }, { value: 'opt2', label: 'Option 2' }],
    // Options for second toggle
    [{ value: 'opt3', label: 'Option 3' }, { value: 'opt4', label: 'Option 4' }],
    // Options for third toggle
    [{ value: 'opt5', label: 'Option 5' }, { value: 'opt6', label: 'Option 6' }]
  ];
}

Performance Optimization

Change Detection Strategy

The demo component uses ChangeDetectionStrategy.OnPush for optimal performance:

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  // ... other config
})
export class ButtonToggleDemoComponent {
  // Component logic
}

Performance Tips

  1. Use OnPush change detection for better performance with large toggle arrays
  2. Implement trackBy for dynamic toggle lists
  3. Avoid frequent data object recreation to prevent unnecessary re-renders
  4. Use immutable data patterns for toggle option updates
  5. Consider virtual scrolling for very large toggle lists
  6. Optimize tooltip content to avoid performance issues

Testing

Unit Testing Example

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ButtonToggleInputComponent } from './button-toggle-input.component';
import { ListItem } from './models/list-item.model';
import { ReactiveFormsModule } from '@angular/forms';

describe('ButtonToggleInputComponent', () => {
  let component: ButtonToggleInputComponent;
  let fixture: ComponentFixture<ButtonToggleInputComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ ButtonToggleInputComponent ],
      imports: [ ReactiveFormsModule ]
    }).compileComponents();

    fixture = TestBed.createComponent(ButtonToggleInputComponent);
    component = fixture.componentInstance;
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should display toggle items from data input', () => {
    component.data = [
      { value: 'opt1', label: 'Option 1' },
      { value: 'opt2', label: 'Option 2' }
    ];
    fixture.detectChanges();
    
    const compiled = fixture.nativeElement;
    const buttons = compiled.querySelectorAll('.mat-button-toggle');
    expect(buttons.length).toBe(2);
  });

  it('should emit value changes through form control', () => {
    const formControl = new FormControl();
    component.formControl = formControl;
    
    spyOn(formControl, 'setValue');
    component.writeValue('test-value');
    
    expect(formControl.setValue).toHaveBeenCalledWith('test-value');
  });

  it('should support multiple selection mode', () => {
    component.multiple = true;
    component.data = [
      { value: 'opt1', label: 'Option 1' },
      { value: 'opt2', label: 'Option 2' }
    ];
    fixture.detectChanges();
    
    // Test multiple selection logic
    expect(component.multiple).toBe(true);
  });

  it('should handle tooltips correctly', () => {
    component.toolTips = true;
    component.data = [
      { value: 'opt1', label: 'Option 1', tootTip: 'Tooltip 1' }
    ];
    fixture.detectChanges();
    
    const compiled = fixture.nativeElement;
    const tooltip = compiled.querySelector('[mattooltip]');
    expect(tooltip).toBeTruthy();
  });
});

Integration Testing

import { TestBed, ComponentFixture } from '@angular/core/testing';
import { ButtonToggleInputModule } from './button-toggle-input.module';

describe('ButtonToggleInput Integration', () => {
  let component: TestHostComponent;
  let fixture: ComponentFixture<TestHostComponent>;

  @Component({
    template: `
      <app-button-toggle-input
        [formControl]="testControl"
        [data]="testData"
        [multiple]="true">
      </app-button-toggle-input>
    `
  })
  class TestHostComponent {
    testControl = new FormControl();
    testData: ListItem[] = [
      { value: 'test1', label: 'Test 1' },
      { value: 'test2', label: 'Test 2' }
    ];
  }

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ TestHostComponent ],
      imports: [ ButtonToggleInputModule ]
    }).compileComponents();

    fixture = TestBed.createComponent(TestHostComponent);
    component = fixture.componentInstance;
  });

  it('should integrate with form controls', () => {
    expect(component.testControl).toBeDefined();
    expect(component.testData.length).toBe(2);
  });

  it('should update form control value when selection changes', () => {
    fixture.detectChanges();
    
    // Simulate user interaction
    const buttons = fixture.nativeElement.querySelectorAll('.mat-button-toggle');
    buttons[0].click();
    
    expect(component.testControl.value).toEqual(['test1']);
  });
});

Troubleshooting

Common Issues

  1. Form control not working: Ensure ReactiveFormsModule is imported
  2. Icons not displaying: Verify MatIconModule is imported and Material Icons are loaded
  3. Tooltips not showing: Check that MatTooltipModule is imported
  4. Styling issues: Ensure Material theme is properly configured
  5. Change detection warnings: Consider using OnPush change detection strategy

Debug Mode

// Add debugging to track form control changes
@Component({
  template: `
    <div class="debug-info">
      Form Control Value: {{ formControl.value | json }}<br>
      Form Control Valid: {{ formControl.valid }}<br>
      Data Length: {{ data?.length || 0 }}<br>
      Multiple Mode: {{ multiple }}
    </div>
    
    <app-button-toggle-input
      [formControl]="formControl"
      [data]="data"
      [multiple]="multiple">
    </app-button-toggle-input>
  `
})
export class DebugToggleComponent {
  formControl = new FormControl();
  data: ListItem[] = [];
  multiple = false;
  
  constructor() {
    this.formControl.valueChanges.subscribe(value => {
      console.log('Toggle value changed:', value);
    });
  }
}

Performance Issues

// Optimize for large datasets
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <app-button-toggle-input
      [formControl]="formControl"
      [data]="data"
      [multiple]="multiple">
    </app-button-toggle-input>
  `
})
export class OptimizedToggleComponent {
  // Use immutable data updates
  updateData(newData: ListItem[]) {
    this.data = [...newData]; // Create new array reference
  }
}