# @isa/ui/bullet-list

A lightweight bullet list component system for Angular applications supporting customizable icons and hierarchical content presentation.

## Overview

The Bullet List library provides a pair of components (`ui-bullet-list` and `ui-bullet-list-item`) that work together to create visually consistent bullet-point lists with icon support. The system uses Angular's host-based component injection to propagate icon configuration from parent to children, enabling both uniform and customized list appearances.

## Table of Contents

- [Features](#features)
- [Quick Start](#quick-start)
- [Component API](#component-api)
- [Usage Examples](#usage-examples)
- [Styling and Customization](#styling-and-customization)
- [Architecture Notes](#architecture-notes)
- [Testing](#testing)
- [Dependencies](#dependencies)

## Features

- **Parent-child icon inheritance** - Default icon flows from list to items
- **Per-item icon override** - Individual items can specify custom icons
- **Angular signals integration** - Reactive icon computation with `computed()`
- **Standalone components** - Modern Angular architecture with explicit imports
- **Icon library integration** - Uses `@ng-icons/core` with ISA icon set
- **OnPush change detection** - Optimized performance
- **Host-based injection** - Seamless parent-child communication
- **Content projection** - Flexible content rendering via `<ng-content>`

## Quick Start

### 1. Import Components

```typescript
import { Component } from '@angular/core';
import { BulletListComponent, BulletListItemComponent } from '@isa/ui/bullet-list';

@Component({
  selector: 'app-feature-list',
  imports: [BulletListComponent, BulletListItemComponent],
  template: `...`
})
export class FeatureListComponent {}
```

### 2. Basic Usage

```html
<ui-bullet-list>
  <ui-bullet-list-item>First feature</ui-bullet-list-item>
  <ui-bullet-list-item>Second feature</ui-bullet-list-item>
  <ui-bullet-list-item>Third feature</ui-bullet-list-item>
</ui-bullet-list>
```

### 3. Custom Icons

```html
<!-- Custom icon for entire list -->
<ui-bullet-list [icon]="'isaActionCheck'">
  <ui-bullet-list-item>Completed task</ui-bullet-list-item>
  <ui-bullet-list-item>Another completed task</ui-bullet-list-item>
</ui-bullet-list>

<!-- Mixed icons - per-item override -->
<ui-bullet-list>
  <ui-bullet-list-item [icon]="'isaActionCheck'">Done</ui-bullet-list-item>
  <ui-bullet-list-item [icon]="'isaActionClock'">In progress</ui-bullet-list-item>
  <ui-bullet-list-item>Default icon</ui-bullet-list-item>
</ui-bullet-list>
```

## Component API

### BulletListComponent

Container component for bullet list items.

#### Selector
```typescript
'ui-bullet-list'
```

#### Inputs

| Input | Type | Default | Description |
|-------|------|---------|-------------|
| `icon` | `string` | `'isaActionChevronRight'` | Default icon name for all child items |

#### Host Bindings
- `class`: `'ui-bullet-list'` - Base CSS class for styling

#### Template
Content projection only - wraps child items:
```html
<ng-content></ng-content>
```

#### Providers
Automatically provides `isaActionChevronRight` icon to the icon registry.

---

### BulletListItemComponent

Individual item within a bullet list.

#### Selector
```typescript
'ui-bullet-list-item'
```

#### Inputs

| Input | Type | Default | Description |
|-------|------|---------|-------------|
| `icon` | `string \| undefined` | `undefined` | Custom icon override (uses parent icon if undefined) |

#### Computed Properties

| Property | Type | Description |
|----------|------|-------------|
| `iconName()` | `string \| undefined` | Computed icon name - resolves to item icon or falls back to parent icon |

#### Host Bindings
- `class`: `'ui-bullet-list-item'` - Base CSS class for styling

#### Template
```html
<ng-icon [name]="iconName()" size="1.5rem"></ng-icon>
<ng-content></ng-content>
```

## Usage Examples

### Basic Feature List

```typescript
import { Component } from '@angular/core';
import { BulletListComponent, BulletListItemComponent } from '@isa/ui/bullet-list';

@Component({
  selector: 'app-product-features',
  imports: [BulletListComponent, BulletListItemComponent],
  template: `
    <h2>Product Features</h2>
    <ui-bullet-list>
      <ui-bullet-list-item>Fast performance</ui-bullet-list-item>
      <ui-bullet-list-item>Easy to use</ui-bullet-list-item>
      <ui-bullet-list-item>Highly customizable</ui-bullet-list-item>
    </ui-bullet-list>
  `
})
export class ProductFeaturesComponent {}
```

### Custom List Icon

```typescript
@Component({
  selector: 'app-task-list',
  imports: [BulletListComponent, BulletListItemComponent],
  template: `
    <h2>Completed Tasks</h2>
    <ui-bullet-list [icon]="'isaActionCheck'">
      <ui-bullet-list-item>Project setup complete</ui-bullet-list-item>
      <ui-bullet-list-item>Tests passing</ui-bullet-list-item>
      <ui-bullet-list-item>Documentation updated</ui-bullet-list-item>
    </ui-bullet-list>
  `
})
export class TaskListComponent {}
```

### Mixed Icons with Status Indicators

```typescript
@Component({
  selector: 'app-status-list',
  imports: [BulletListComponent, BulletListItemComponent],
  template: `
    <h2>Task Status</h2>
    <ui-bullet-list>
      <ui-bullet-list-item [icon]="'isaActionCheck'">
        Authentication implemented
      </ui-bullet-list-item>
      <ui-bullet-list-item [icon]="'isaActionClock'">
        API integration in progress
      </ui-bullet-list-item>
      <ui-bullet-list-item [icon]="'isaActionWarning'">
        Performance testing pending
      </ui-bullet-list-item>
    </ui-bullet-list>
  `
})
export class StatusListComponent {}
```

### Dynamic Icon Selection

```typescript
import { Component, signal } from '@angular/core';
import { BulletListComponent, BulletListItemComponent } from '@isa/ui/bullet-list';

@Component({
  selector: 'app-dynamic-list',
  imports: [BulletListComponent, BulletListItemComponent],
  template: `
    <ui-bullet-list [icon]="currentIcon()">
      <ui-bullet-list-item>Item 1</ui-bullet-list-item>
      <ui-bullet-list-item>Item 2</ui-bullet-list-item>
      <ui-bullet-list-item>Item 3</ui-bullet-list-item>
    </ui-bullet-list>

    <button (click)="changeIcon()">Toggle Icon</button>
  `
})
export class DynamicListComponent {
  currentIcon = signal('isaActionChevronRight');

  changeIcon() {
    const newIcon = this.currentIcon() === 'isaActionChevronRight'
      ? 'isaActionCheck'
      : 'isaActionChevronRight';
    this.currentIcon.set(newIcon);
  }
}
```

### Nested Content with Rich HTML

```typescript
@Component({
  selector: 'app-rich-list',
  imports: [BulletListComponent, BulletListItemComponent],
  template: `
    <ui-bullet-list>
      <ui-bullet-list-item>
        <strong>Bold heading:</strong> Regular text description
      </ui-bullet-list-item>
      <ui-bullet-list-item>
        <a href="/docs">Link to documentation</a>
      </ui-bullet-list-item>
      <ui-bullet-list-item>
        <span class="highlight">Highlighted content</span> with normal text
      </ui-bullet-list-item>
    </ui-bullet-list>
  `
})
export class RichListComponent {}
```

### Conditional Items with Control Flow

```typescript
import { Component, signal } from '@angular/core';
import { BulletListComponent, BulletListItemComponent } from '@isa/ui/bullet-list';

@Component({
  selector: 'app-conditional-list',
  imports: [BulletListComponent, BulletListItemComponent],
  template: `
    <ui-bullet-list>
      <ui-bullet-list-item>Always visible</ui-bullet-list-item>

      @if (showAdditional()) {
        <ui-bullet-list-item>Conditional item 1</ui-bullet-list-item>
        <ui-bullet-list-item>Conditional item 2</ui-bullet-list-item>
      }

      @for (item of dynamicItems(); track item.id) {
        <ui-bullet-list-item [icon]="item.icon">
          {{ item.text }}
        </ui-bullet-list-item>
      }
    </ui-bullet-list>
  `
})
export class ConditionalListComponent {
  showAdditional = signal(true);
  dynamicItems = signal([
    { id: 1, text: 'Dynamic 1', icon: 'isaActionCheck' },
    { id: 2, text: 'Dynamic 2', icon: 'isaActionClock' }
  ]);
}
```

## Styling and Customization

### CSS Classes

The components expose the following CSS classes for styling:

```css
/* List container */
.ui-bullet-list {
  /* Add spacing, layout, etc. */
}

/* Individual list items */
.ui-bullet-list-item {
  /* Style item layout */
}

/* Icon within items (via ng-icon) */
.ui-bullet-list-item ng-icon {
  /* Customize icon appearance */
}
```

### Example Styling

```scss
// Custom spacing and layout
.ui-bullet-list {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.ui-bullet-list-item {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;

  ng-icon {
    flex-shrink: 0;
    margin-top: 0.125rem; // Align with text
    color: var(--isa-accent-500);
  }
}

// Status-specific styling
.ui-bullet-list-item {
  &.success ng-icon {
    color: var(--isa-success-500);
  }

  &.warning ng-icon {
    color: var(--isa-warning-500);
  }

  &.error ng-icon {
    color: var(--isa-error-500);
  }
}
```

### Icon Size Customization

The default icon size is `1.5rem`. To customize globally:

```scss
.ui-bullet-list-item ng-icon {
  font-size: 1.25rem; // Smaller icons
  width: 1.25rem;
  height: 1.25rem;
}
```

## Architecture Notes

### Parent-Child Communication Pattern

The library uses Angular's dependency injection with the `host` flag to enable seamless communication:

```typescript
// In BulletListItemComponent
#bulletList = inject(BulletListComponent, { optional: true, host: true });
```

This pattern:
- Only injects the **direct parent** `BulletListComponent`
- Returns `null` if no parent exists (via `optional: true`)
- Enables icon inheritance without prop drilling

### Icon Resolution Logic

```typescript
iconName = computed(() => {
  return this.icon() ?? this.#bulletList?.icon();
});
```

Resolution order:
1. Use item's own `icon` input if provided
2. Fall back to parent list's `icon` input
3. Fall back to default `'isaActionChevronRight'`

### Change Detection Strategy

Both components use `OnPush` change detection for optimal performance:
- Changes only trigger when inputs change
- Computed signals automatically track dependencies
- No manual change detection needed

### Standalone Architecture

Components are fully standalone with explicit imports:
```typescript
imports: [NgIcon]  // BulletListItemComponent
imports: []        // BulletListComponent (no dependencies)
```

## Testing

The library uses **Vitest** with **Angular Testing Utilities** for testing.

### Running Tests

```bash
# Run tests for this library
npx nx test ui-bullet-list --skip-nx-cache

# Run tests with coverage
npx nx test ui-bullet-list --code-coverage --skip-nx-cache

# Run tests in watch mode
npx nx test ui-bullet-list --watch
```

### Test Coverage

The library includes comprehensive tests covering:

- **Component rendering** - Verifies components render correctly
- **Icon inheritance** - Tests parent-to-child icon propagation
- **Icon override** - Tests per-item icon customization
- **Content projection** - Validates ng-content rendering
- **Host injection** - Tests optional host injection behavior
- **Signal reactivity** - Tests computed signal updates

### Example Test

```typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { describe, it, expect, beforeEach } from 'vitest';
import { BulletListComponent, BulletListItemComponent } from '@isa/ui/bullet-list';

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

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [BulletListItemComponent, BulletListComponent]
    });
    fixture = TestBed.createComponent(BulletListItemComponent);
    component = fixture.componentInstance;
  });

  it('should use parent icon when no icon provided', () => {
    // Arrange: Create parent list with custom icon
    const parentFixture = TestBed.createComponent(BulletListComponent);
    parentFixture.componentInstance.icon.set('isaActionCheck');

    // Act: Create child item
    const childFixture = TestBed.createComponent(BulletListItemComponent);

    // Assert: Child should inherit parent icon
    expect(childFixture.componentInstance.iconName()).toBe('isaActionCheck');
  });

  it('should override parent icon when icon input provided', () => {
    // Arrange & Act
    component.icon.set('isaActionWarning');
    fixture.detectChanges();

    // Assert
    expect(component.iconName()).toBe('isaActionWarning');
  });
});
```

## Dependencies

### Required Libraries

- `@angular/core` - Angular framework (v20.1.2)
- `@ng-icons/core` - Icon component library
- `@isa/icons` - ISA icon set (provides `isaActionChevronRight`)

### Path Alias

Import from: `@isa/ui/bullet-list`

### Peer Dependencies

The components require the icon library to be properly configured at the application level. Ensure `@ng-icons/core` is installed and configured.

## Best Practices

1. **Consistent Icon Usage** - Use the same icon set throughout your application for visual consistency
2. **Semantic Icons** - Choose icons that match the context (check marks for completed items, chevrons for navigation)
3. **Accessibility** - Ensure icon meanings are clear from surrounding text
4. **Performance** - The OnPush strategy means you can safely use these components in large lists
5. **Content Structure** - Keep list items concise; use nested HTML for complex content
6. **Icon Registration** - Only icons used in the list need to be registered (parent component handles default)

## License

Internal ISA Frontend library - not for external distribution.
