Files
Lorenz Hilpert 2b5da00249 feat(checkout): add reward order confirmation feature with schema migrations
- Add new reward-order-confirmation feature library with components and store
- Implement checkout completion orchestrator service for order finalization
- Migrate checkout/oms/crm models to Zod schemas for better type safety
- Add order creation facade and display order schemas
- Update shopping cart facade with order completion flow
- Add comprehensive tests for shopping cart facade
- Update routing to include order confirmation page
2025-10-21 14:28:52 +02:00
..
2025-07-14 11:57:03 +00:00

@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

  • 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

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

<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

<!-- 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

'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:

<ng-content></ng-content>

Providers

Automatically provides isaActionChevronRight icon to the icon registry.


BulletListItemComponent

Individual item within a bullet list.

Selector

'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

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

Usage Examples

Basic Feature List

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

@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

@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

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

@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

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:

/* 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

// 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:

.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:

// 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

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:

imports: [NgIcon]  // BulletListItemComponent
imports: []        // BulletListComponent (no dependencies)

Testing

The library uses Vitest with Angular Testing Utilities for testing.

Running Tests

# 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

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.