Building and Testing an Angular Accordion Component

25-08-2023
source code

Building and Testing an Angular Accordion Component

Introduction

Accordion components are indispensable in modern web applications, offering an interactive way to display content compactly. This guide delves into creating a reusable accordion component using Angular, focusing on clean architecture, functionality, and essential unit testing to verify its reliability.

PanelComponent Implementation

The PanelComponent represents individual sections of the accordion, capable of expanding or collapsing to show or hide content.

import { Component, Input, OnInit, EventEmitter, ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'app-panel',
  templateUrl: './panel.component.html',
  styleUrls: ['./panel.component.scss']
})
export class PanelComponent implements OnInit {
  @Input() body: string;
  @Input() title: string;
  hidden = false;
  btnClick = new EventEmitter();

  constructor(private ref: ChangeDetectorRef) {}

  ngOnInit(): void {}

  handleButtonClick(): void {
    this.hidden = !this.hidden;
    this.btnClick.emit(this.hidden);
    this.ref.detectChanges();
  }
}

AccordionComponent Implementation

The AccordionComponent manages multiple PanelComponent instances, ensuring that at most one panel is expanded at any time.

import { AfterContentInit, Component, ContentChildren, QueryList } from '@angular/core';
import { PanelComponent } from './panel/panel.component';

@Component({
  selector: 'app-accordion',
  templateUrl: './accordion.component.html',
  styleUrls: ['./accordion.component.scss']
})
export class AccordionComponent implements AfterContentInit {
  @ContentChildren(PanelComponent) panels: QueryList;

  ngAfterContentInit(): void {
    this.panels.forEach(panel => {
      panel.btnClick.subscribe(() => this.togglePanel(panel));
    });
  }

  togglePanel(clickedPanel: PanelComponent): void {
    this.panels.forEach(panel => {
      if (panel !== clickedPanel) panel.hidden = true;
    });
    clickedPanel.hidden = !clickedPanel.hidden;
  }
}

Ensuring Component Integrity through Unit Testing

Unit tests play a vital role in validating the functionality of components in isolation, ensuring they perform as expected under various scenarios.

Setting Up the Test Environment for AccordionComponent

Configuring the testing module involves importing the components and setting up the TestBed:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AccordionComponent } from './accordion.component';
import { PanelComponent } from './panel/panel.component';

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

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [AccordionComponent, PanelComponent]
    })
    .compileComponents();

    fixture = TestBed.createComponent(AccordionComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  // Test cases will go here
});

Testing Panel Expansion and Collapse Logic

A crucial feature of the accordion is its ability to manage the open state of panels correctly. Here's how you might test this functionality:

it('should toggle the hidden state of a panel when it is clicked', () => {
  const panelElements = fixture.debugElement.queryAll(By.directive(PanelComponent));
  const firstPanelComponentInstance = panelElements[0].componentInstance;
  const secondPanelComponentInstance = panelElements[1].componentInstance;

  // Simulate clicking the first panel
  firstPanelComponentInstance.handleButtonClick();
  expect(firstPanelComponentInstance.hidden).toBeFalse();

  // Simulate clicking the second panel, which should close the first panel
  secondPanelComponentInstance.handleButtonClick();
  expect(firstPanelComponentInstance.hidden).toBeTrue();
  expect(secondPanelComponentInstance.hidden).toBeFalse();
});

Conclusion

Developing and thoroughly testing an Angular accordion component not only enhances the user experience by providing an interactive way to display content but also ensures the component's reliability and functionality through rigorous testing. By following these guidelines and implementing the provided code snippets, developers can build sophisticated, interactive web components that stand the test of time.