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

@isa/ui/item-rows

A collection of reusable row components for displaying structured data with consistent layouts across Angular applications.

Overview

The Item Rows UI library provides three specialized row components for presenting structured information in a consistent, accessible format. These components enable standardized display of product items, client information, and key-value data pairs throughout the ISA application. Each component uses content projection and directive-based composition for maximum flexibility while maintaining design system consistency.

Table of Contents

Features

  • Three row components - ItemRowComponent, ItemRowDataComponent, and ClientRowComponent
  • Directive-based composition - Flexible data organization with semantic directives
  • Content projection - Full control over row content via ng-content
  • Convenience imports - Pre-bundled directive arrays for easy imports
  • OnPush change detection - Optimized rendering performance
  • ViewEncapsulation.None - Global styling integration with design system
  • BEM-like CSS naming - Predictable class structure for styling
  • Standalone architecture - Modern Angular components with explicit imports
  • Type-safe directives - Directive selectors support both attribute and element syntax
  • SCSS integration - Component-scoped styles with BEM conventions

Quick Start

1. Basic Item Row

import { Component } from '@angular/core';
import { ItemRowComponent } from '@isa/ui/item-rows';

@Component({
  selector: 'app-product-list-item',
  template: `
    <ui-item-row>
      <h3>Product Name</h3>
      <p>Product description and details</p>
    </ui-item-row>
  `,
  imports: [ItemRowComponent]
})
export class ProductListItemComponent {}

2. Item Row with Data Pairs

import { Component } from '@angular/core';
import { ItemRowDataImports } from '@isa/ui/item-rows';

@Component({
  selector: 'app-product-details',
  template: `
    <ui-item-row-data>
      <div uiItemRowDataRow>
        <span uiItemRowDataLabel>Price:</span>
        <span uiItemRowDataValue>$29.99</span>
      </div>
      <div uiItemRowDataRow>
        <span uiItemRowDataLabel>Stock:</span>
        <span uiItemRowDataValue>In Stock</span>
      </div>
    </ui-item-row-data>
  `,
  imports: [ItemRowDataImports]
})
export class ProductDetailsComponent {}

3. Client Row

import { Component } from '@angular/core';
import { ClientRowImports } from '@isa/ui/item-rows';

@Component({
  selector: 'app-customer-card',
  template: `
    <ui-client-row>
      <div uiClientRowContent>
        <h4>Customer Name</h4>
        <p>customer@example.com</p>
      </div>
    </ui-client-row>
  `,
  imports: [ClientRowImports]
})
export class CustomerCardComponent {}

Core Concepts

Component Types

The library provides three distinct row components, each optimized for specific use cases:

1. ItemRowComponent

  • Purpose: General-purpose row container for any item content
  • Use Cases: Product listings, order items, search results, general lists
  • Structure: Simple container with no internal layout structure
  • CSS Class: ui-item-row
  • Selectors: <ui-item-row> or [ui-item-row]

2. ItemRowDataComponent with Directives

  • Purpose: Key-value data presentation with label/value pairs
  • Use Cases: Product specifications, order details, metadata display
  • Structure: Hierarchical with row/label/value organization
  • CSS Classes:
    • ui-item-row-data (container)
    • ui-item-row-data-row (individual row)
    • ui-item-row-data-label (left-side label)
    • ui-item-row-data-value (right-side value)
  • Directives:
    • ItemRowDataComponent - Main container
    • ItemRowDataRowDirective - Individual data row
    • ItemRowDataLabelDirective - Label element
    • ItemRowDataValueDirective - Value element

3. ClientRowComponent

  • Purpose: Customer/client information display
  • Use Cases: Customer lists, client cards, user profiles
  • Structure: Specialized layout for client-specific content
  • CSS Classes:
    • ui-client-row (container)
    • ui-client-row-content (content wrapper)
  • Directives:
    • ClientRowComponent - Main container
    • ClientRowContentDirective - Content wrapper

Directive-Based Composition

The library uses directives to create semantic, reusable layout patterns:

// Attribute syntax (preferred)
<div uiItemRowDataRow>
  <span uiItemRowDataLabel>Label</span>
  <span uiItemRowDataValue>Value</span>
</div>

// Element syntax (alternative)
<ui-item-row-data-row>
  <ui-item-row-data-label>Label</ui-item-row-data-label>
  <ui-item-row-data-value>Value</ui-item-row-data-value>
</ui-item-row-data-row>

Benefits of directive composition:

  • Semantic HTML - Clear intent in templates
  • Flexible markup - Use with any HTML element
  • Type safety - Directive selectors enforce correct usage
  • Consistent styling - Predictable CSS classes

Convenience Import Arrays

The library provides pre-bundled directive arrays for simplified imports:

// ItemRowDataImports - All data row components and directives
export const ItemRowDataImports = [
  ItemRowDataComponent,
  ItemRowDataLabelDirective,
  ItemRowDataValueDirective,
  ItemRowDataRowDirective,
];

// ClientRowImports - Client row component and content directive
export const ClientRowImports = [
  ClientRowComponent,
  ClientRowContentDirective
];

Usage:

@Component({
  // Import entire directive family at once
  imports: [ItemRowDataImports]
})

API Reference

ItemRowComponent

Basic row container for general item display.

Selector

<ui-item-row>Content</ui-item-row>
<!-- OR -->
<div ui-item-row>Content</div>

Properties

  • No inputs - Pure container component
  • Host class: ui-item-row
  • Change Detection: OnPush
  • Encapsulation: None

Template

<ng-content></ng-content>

Example

<ui-item-row>
  <div class="product-header">
    <h3>Product Title</h3>
    <span class="price">$99.99</span>
  </div>
  <p class="description">Product description text</p>
</ui-item-row>

ItemRowDataComponent

Container for key-value data pairs with semantic directives.

Selector

<ui-item-row-data>Content</ui-item-row-data>

Properties

  • No inputs - Container with directive-based children
  • Host class: ui-item-row-data
  • Change Detection: OnPush
  • Encapsulation: None

Template

<ng-content></ng-content>

Example

<ui-item-row-data>
  <div uiItemRowDataRow>
    <span uiItemRowDataLabel>SKU:</span>
    <span uiItemRowDataValue>ABC-12345</span>
  </div>
</ui-item-row-data>

ItemRowDataRowDirective

Marks an element as a data row within ItemRowDataComponent.

Selectors

<div uiItemRowDataRow>Content</div>
<!-- OR -->
<ui-item-row-data-row>Content</ui-item-row-data-row>

Properties

  • No inputs - Structural directive
  • Host class: ui-item-row-data-row

Example

<div uiItemRowDataRow>
  <span uiItemRowDataLabel>Weight:</span>
  <span uiItemRowDataValue>2.5 kg</span>
</div>

ItemRowDataLabelDirective

Marks an element as a label (left side) in a data row.

Selectors

<span uiItemRowDataLabel>Label Text</span>
<!-- OR -->
<ui-item-row-data-label>Label Text</ui-item-row-data-label>

Properties

  • No inputs - Structural directive
  • Host class: ui-item-row-data-label

Example

<span uiItemRowDataLabel>Category:</span>

ItemRowDataValueDirective

Marks an element as a value (right side) in a data row.

Selectors

<span uiItemRowDataValue>Value Text</span>
<!-- OR -->
<ui-item-row-data-value>Value Text</ui-item-row-data-value>

Properties

  • No inputs - Structural directive
  • Host class: ui-item-row-data-value

Example

<span uiItemRowDataValue>Electronics</span>

ClientRowComponent

Specialized row component for client/customer information.

Selector

<ui-client-row>Content</ui-client-row>

Properties

  • No inputs - Container component
  • Host class: ui-client-row
  • Change Detection: OnPush
  • Encapsulation: None
  • Standalone: true

Template

<ng-content></ng-content>

Example

<ui-client-row>
  <div uiClientRowContent>
    <h4>John Doe</h4>
    <p>john.doe@example.com</p>
  </div>
</ui-client-row>

ClientRowContentDirective

Marks an element as content within ClientRowComponent.

Selectors

<div uiClientRowContent>Content</div>
<!-- OR -->
<ui-client-row-content>Content</ui-client-row-content>

Properties

  • No inputs - Structural directive
  • Host class: ui-client-row-content

Example

<div uiClientRowContent>
  <div class="client-info">
    <span class="client-name">Jane Smith</span>
    <span class="client-id">#12345</span>
  </div>
</div>

Convenience Import Arrays

ItemRowDataImports

Bundle of all data row components and directives.

export const ItemRowDataImports = [
  ItemRowDataComponent,
  ItemRowDataLabelDirective,
  ItemRowDataValueDirective,
  ItemRowDataRowDirective,
];

Usage:

@Component({
  imports: [ItemRowDataImports]
})

ClientRowImports

Bundle of client row component and directive.

export const ClientRowImports = [
  ClientRowComponent,
  ClientRowContentDirective
];

Usage:

@Component({
  imports: [ClientRowImports]
})

Usage Examples

Product Information Display

import { Component, input } from '@angular/core';
import { ItemRowComponent, ItemRowDataImports } from '@isa/ui/item-rows';

interface Product {
  id: number;
  name: string;
  sku: string;
  price: number;
  stock: number;
  category: string;
}

@Component({
  selector: 'app-product-info',
  template: `
    <ui-item-row>
      <h3>{{ product().name }}</h3>

      <ui-item-row-data>
        <div uiItemRowDataRow>
          <span uiItemRowDataLabel>SKU:</span>
          <span uiItemRowDataValue>{{ product().sku }}</span>
        </div>

        <div uiItemRowDataRow>
          <span uiItemRowDataLabel>Price:</span>
          <span uiItemRowDataValue>\${{ product().price }}</span>
        </div>

        <div uiItemRowDataRow>
          <span uiItemRowDataLabel>Stock:</span>
          <span uiItemRowDataValue>{{ product().stock }} units</span>
        </div>

        <div uiItemRowDataRow>
          <span uiItemRowDataLabel>Category:</span>
          <span uiItemRowDataValue>{{ product().category }}</span>
        </div>
      </ui-item-row-data>
    </ui-item-row>
  `,
  imports: [ItemRowComponent, ItemRowDataImports]
})
export class ProductInfoComponent {
  product = input.required<Product>();
}

Order Details List

import { Component, input } from '@angular/core';
import { ItemRowDataImports } from '@isa/ui/item-rows';
import { DatePipe, CurrencyPipe } from '@angular/common';

interface Order {
  orderNumber: string;
  date: Date;
  status: string;
  total: number;
  shippingMethod: string;
}

@Component({
  selector: 'app-order-details',
  template: `
    <ui-item-row-data>
      <div uiItemRowDataRow>
        <span uiItemRowDataLabel>Order Number:</span>
        <strong uiItemRowDataValue>{{ order().orderNumber }}</strong>
      </div>

      <div uiItemRowDataRow>
        <span uiItemRowDataLabel>Order Date:</span>
        <span uiItemRowDataValue>{{ order().date | date:'short' }}</span>
      </div>

      <div uiItemRowDataRow>
        <span uiItemRowDataLabel>Status:</span>
        <span uiItemRowDataValue class="status-{{ order().status }}">
          {{ order().status }}
        </span>
      </div>

      <div uiItemRowDataRow>
        <span uiItemRowDataLabel>Total:</span>
        <span uiItemRowDataValue>{{ order().total | currency }}</span>
      </div>

      <div uiItemRowDataRow>
        <span uiItemRowDataLabel>Shipping:</span>
        <span uiItemRowDataValue>{{ order().shippingMethod }}</span>
      </div>
    </ui-item-row-data>
  `,
  imports: [ItemRowDataImports, DatePipe, CurrencyPipe]
})
export class OrderDetailsComponent {
  order = input.required<Order>();
}

Customer List with Client Rows

import { Component, signal } from '@angular/core';
import { ClientRowImports } from '@isa/ui/item-rows';

interface Customer {
  id: number;
  name: string;
  email: string;
  phone: string;
  company: string;
}

@Component({
  selector: 'app-customer-list',
  template: `
    <div class="customer-list">
      @for (customer of customers(); track customer.id) {
        <ui-client-row>
          <div uiClientRowContent class="customer-card">
            <div class="customer-header">
              <h4>{{ customer.name }}</h4>
              <span class="customer-id">#{{ customer.id }}</span>
            </div>
            <div class="customer-details">
              <p>{{ customer.email }}</p>
              <p>{{ customer.phone }}</p>
              <p class="company">{{ customer.company }}</p>
            </div>
          </div>
        </ui-client-row>
      }
    </div>
  `,
  imports: [ClientRowImports],
  styles: [`
    .customer-list {
      display: flex;
      flex-direction: column;
      gap: 1rem;
    }

    .customer-card {
      padding: 1rem;
    }

    .customer-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 0.5rem;
    }

    .company {
      color: var(--isa-text-secondary);
      font-size: 0.875rem;
    }
  `]
})
export class CustomerListComponent {
  customers = signal<Customer[]>([
    {
      id: 1,
      name: 'John Doe',
      email: 'john@example.com',
      phone: '+1 555-0100',
      company: 'Acme Corp'
    },
    // ... more customers
  ]);
}

Mixed Content with Conditional Rendering

import { Component, input, computed } from '@angular/core';
import { ItemRowComponent, ItemRowDataImports } from '@isa/ui/item-rows';

interface ProductWithVariants {
  name: string;
  basePrice: number;
  hasVariants: boolean;
  variants?: Array<{ name: string; price: number }>;
  specifications: Record<string, string>;
}

@Component({
  selector: 'app-product-variant-info',
  template: `
    <ui-item-row>
      <h3>{{ product().name }}</h3>

      @if (product().hasVariants) {
        <div class="variants">
          @for (variant of product().variants; track variant.name) {
            <span class="variant-chip">
              {{ variant.name }} - \${{ variant.price }}
            </span>
          }
        </div>
      }

      <ui-item-row-data>
        @for (spec of specificationEntries(); track spec.key) {
          <div uiItemRowDataRow>
            <span uiItemRowDataLabel>{{ spec.key }}:</span>
            <span uiItemRowDataValue>{{ spec.value }}</span>
          </div>
        }
      </ui-item-row-data>
    </ui-item-row>
  `,
  imports: [ItemRowComponent, ItemRowDataImports]
})
export class ProductVariantInfoComponent {
  product = input.required<ProductWithVariants>();

  specificationEntries = computed(() =>
    Object.entries(this.product().specifications).map(([key, value]) => ({
      key,
      value
    }))
  );
}

Nested Row Structures

import { Component } from '@angular/core';
import { ItemRowComponent, ItemRowDataImports } from '@isa/ui/item-rows';

@Component({
  selector: 'app-order-item-detail',
  template: `
    <ui-item-row class="order-item">
      <!-- Product header -->
      <div class="item-header">
        <h4>Wireless Headphones</h4>
        <span class="quantity">Qty: 2</span>
      </div>

      <!-- Primary product details -->
      <ui-item-row-data class="primary-details">
        <div uiItemRowDataRow>
          <span uiItemRowDataLabel>Unit Price:</span>
          <span uiItemRowDataValue>$79.99</span>
        </div>

        <div uiItemRowDataRow>
          <span uiItemRowDataLabel>Subtotal:</span>
          <span uiItemRowDataValue>$159.98</span>
        </div>
      </ui-item-row-data>

      <!-- Shipping information -->
      <ui-item-row-data class="shipping-details">
        <div uiItemRowDataRow>
          <span uiItemRowDataLabel>Estimated Delivery:</span>
          <span uiItemRowDataValue>Dec 25, 2025</span>
        </div>

        <div uiItemRowDataRow>
          <span uiItemRowDataLabel>Shipping Method:</span>
          <span uiItemRowDataValue>Standard (3-5 days)</span>
        </div>
      </ui-item-row-data>
    </ui-item-row>
  `,
  imports: [ItemRowComponent, ItemRowDataImports]
})
export class OrderItemDetailComponent {}

Dynamic Data Rows

import { Component, input, computed } from '@angular/core';
import { ItemRowDataImports } from '@isa/ui/item-rows';

@Component({
  selector: 'app-dynamic-metadata',
  template: `
    <ui-item-row-data>
      @for (field of metadataFields(); track field.key) {
        <div uiItemRowDataRow>
          <span uiItemRowDataLabel>{{ field.label }}:</span>
          <span
            uiItemRowDataValue
            [class]="field.highlightClass">
            {{ field.value }}
          </span>
        </div>
      }
    </ui-item-row-data>
  `,
  imports: [ItemRowDataImports]
})
export class DynamicMetadataComponent {
  metadata = input.required<Record<string, any>>();

  metadataFields = computed(() => {
    const data = this.metadata();
    return Object.entries(data).map(([key, value]) => ({
      key,
      label: this.formatLabel(key),
      value: this.formatValue(value),
      highlightClass: this.getHighlightClass(key, value)
    }));
  });

  private formatLabel(key: string): string {
    return key
      .replace(/([A-Z])/g, ' $1')
      .replace(/^./, str => str.toUpperCase());
  }

  private formatValue(value: any): string {
    if (typeof value === 'boolean') return value ? 'Yes' : 'No';
    if (value === null || value === undefined) return 'N/A';
    return String(value);
  }

  private getHighlightClass(key: string, value: any): string {
    if (key === 'status') {
      return value === 'active' ? 'text-success' : 'text-muted';
    }
    return '';
  }
}

Styling and Customization

CSS Class Structure

The components generate predictable BEM-like class structures:

/* ItemRowComponent */
.ui-item-row {
  /* Base item row styles */
  display: block;
  padding: 1rem;
  border-bottom: 1px solid var(--isa-border-color);
}

/* ItemRowDataComponent */
.ui-item-row-data {
  /* Container for data rows */
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.ui-item-row-data-row {
  /* Individual data row */
  display: grid;
  grid-template-columns: minmax(120px, 1fr) 2fr;
  gap: 1rem;
  align-items: start;
}

.ui-item-row-data-label {
  /* Label (left column) */
  font-weight: 500;
  color: var(--isa-text-secondary);
  text-align: left;
}

.ui-item-row-data-value {
  /* Value (right column) */
  color: var(--isa-text-primary);
  text-align: left;
}

/* ClientRowComponent */
.ui-client-row {
  /* Client row container */
  display: block;
  background-color: var(--isa-surface);
  border-radius: 8px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.ui-client-row-content {
  /* Client row content wrapper */
  padding: 1.5rem;
}

Custom Styling Examples

// Custom product row styling
.product-listing {
  ui-item-row {
    background-color: white;
    border-radius: 4px;
    margin-bottom: 1rem;
    transition: box-shadow 0.2s;

    &:hover {
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    }
  }

  .ui-item-row-data {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--isa-border-color);
  }

  .ui-item-row-data-label {
    font-size: 0.875rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
  }

  .ui-item-row-data-value {
    font-weight: 600;
  }
}

// Custom client row styling
.customer-directory {
  ui-client-row {
    margin-bottom: 1rem;
    border: 1px solid var(--isa-border-light);

    &:hover {
      border-color: var(--isa-brand-primary);
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    }
  }

  .ui-client-row-content {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 1rem;
    align-items: center;
  }
}

Responsive Layouts

/* Mobile-first responsive data rows */
.ui-item-row-data-row {
  /* Stack on mobile */
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

@media (min-width: 640px) {
  .ui-item-row-data-row {
    /* Side-by-side on tablet and up */
    flex-direction: row;
    justify-content: space-between;
  }
}

@media (min-width: 1024px) {
  .ui-item-row-data-row {
    /* Grid layout on desktop */
    display: grid;
    grid-template-columns: minmax(150px, 1fr) 3fr;
  }
}

Tailwind CSS Integration

<!-- Using Tailwind classes with item rows -->
<ui-item-row class="p-4 mb-4 bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow">
  <ui-item-row-data class="space-y-2">
    <div uiItemRowDataRow class="grid grid-cols-2 gap-4">
      <span uiItemRowDataLabel class="text-sm font-medium text-gray-600">
        Label
      </span>
      <span uiItemRowDataValue class="text-base text-gray-900">
        Value
      </span>
    </div>
  </ui-item-row-data>
</ui-item-row>

Testing

The library uses Jest for testing (legacy setup - migration to Vitest pending).

Running Tests

# Run tests for this library
npx nx test ui-item-rows --skip-nx-cache

# Run tests with coverage
npx nx test ui-item-rows --code-coverage --skip-nx-cache

# Run tests in watch mode
npx nx test ui-item-rows --watch

Test Structure

The library should include tests covering:

  • Component rendering - Verify components render correctly
  • Content projection - Test ng-content works as expected
  • Directive application - Verify CSS classes are applied correctly
  • Host bindings - Test host class bindings
  • Nested structures - Test complex nesting scenarios

Example Test

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { describe, it, expect, beforeEach } from '@jest/globals';
import {
  ItemRowDataComponent,
  ItemRowDataRowDirective,
  ItemRowDataLabelDirective,
  ItemRowDataValueDirective
} from './item-row-data.component';

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

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        ItemRowDataComponent,
        ItemRowDataRowDirective,
        ItemRowDataLabelDirective,
        ItemRowDataValueDirective
      ]
    });
    fixture = TestBed.createComponent(ItemRowDataComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

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

  it('should have ui-item-row-data class', () => {
    const element = fixture.nativeElement as HTMLElement;
    expect(element.classList.contains('ui-item-row-data')).toBe(true);
  });

  it('should project content', () => {
    const compiled = fixture.nativeElement as HTMLElement;
    const testContent = '<div>Test Content</div>';
    compiled.innerHTML = testContent;
    fixture.detectChanges();
    expect(compiled.innerHTML).toContain('Test Content');
  });
});

Architecture Notes

Current Architecture

The library follows a directive-based composition pattern:

ItemRowComponent (Simple Container)
├── No inputs, pure content projection
└── CSS: ui-item-row

ItemRowDataComponent (Data Container)
├── No inputs, container for data rows
├── CSS: ui-item-row-data
└── Works with directives:
    ├── ItemRowDataRowDirective (row wrapper)
    ├── ItemRowDataLabelDirective (label/key)
    └── ItemRowDataValueDirective (value)

ClientRowComponent (Client Container)
├── No inputs, specialized for clients
├── CSS: ui-client-row
└── Works with directive:
    └── ClientRowContentDirective (content wrapper)

Design Decisions

1. Directive-Based Composition

Decision: Use directives instead of child components for layout structure Rationale:

  • Flexibility to use any HTML element (div, span, p, etc.)
  • Semantic selectors (uiItemRowDataLabel vs generic component)
  • Lighter weight than full components
  • Easier to apply to existing markup

Impact: More flexible templates with clear semantic meaning

2. Dual Selector Support

Decision: Support both attribute and element selectors Rationale:

  • [uiItemRowDataRow] - Attribute syntax for flexible element types
  • <ui-item-row-data-row> - Element syntax for clarity
  • Developers can choose based on preference and context
  • Consistent with Angular best practices

Impact: Better developer experience with choice of syntax

3. Convenience Import Arrays

Decision: Export pre-bundled directive arrays Rationale:

  • Reduces boilerplate in component imports
  • Ensures all related directives are imported together
  • Clear API - import array, not individual directives
  • Prevents missing directive errors

Impact: Simpler, more maintainable component declarations

4. No Component Inputs

Decision: Components have no @Input properties Rationale:

  • Pure structural components focused on layout
  • Content and styling controlled via projection and CSS
  • Simpler API with fewer configuration options
  • Promotes composition over configuration

Impact: Simpler components, styling handled via CSS

5. ViewEncapsulation.None

Decision: Use ViewEncapsulation.None for global CSS classes Rationale:

  • Consistent with ISA design system approach
  • BEM-like naming prevents conflicts
  • Easier theming and customization
  • Works well with Tailwind utility classes

Impact: Flexible styling with global CSS classes

Migration Considerations

Testing Framework Migration (Pending)

Current State: Uses Jest Target: Migrate to Vitest + Angular Testing Utilities Reason: Aligns with ISA codebase standards (13 libraries already migrated) Priority: Low - works well with Jest currently

Migration steps when ready:

  1. Update project.json to use @nx/vite:test
  2. Add vite.config.mts
  3. Convert tests to use Vitest imports
  4. Update test setup files

Performance Considerations

  1. OnPush change detection - Minimal overhead for structural components
  2. No reactive inputs - No signal overhead
  3. Simple templates - Just ng-content projection
  4. Directive overhead - Minimal, just CSS class application
  5. No JavaScript logic - Pure presentational components

Future Enhancements

Potential improvements identified:

  1. Icon Support - Add optional icon directives for labels
  2. Accordion Behavior - Collapsible row variants
  3. Selection Support - Checkbox/radio integration
  4. Loading States - Skeleton variants for data rows
  5. Empty States - Placeholder content when no data
  6. Accessibility - ARIA attributes for screen readers
  7. Animation Support - Entry/exit animations for rows

Dependencies

Required Libraries

  • @angular/core - Angular framework (20.1.2)
  • @angular/common - CommonModule (implicitly used)

Optional Libraries

None - pure presentation components with no external dependencies.

Path Alias

Import from: @isa/ui/item-rows

License

Internal ISA Frontend library - not for external distribution.