- 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
@isa/ui/label
A flexible label component for displaying tags and notices with configurable priority levels across Angular applications.
Overview
The Label UI library provides a standalone Angular component for displaying visual labels with semantic meaning. It supports two distinct label types (Tag and Notice) and three priority levels (High, Medium, Low), enabling consistent visual communication of information status, categorization, and importance throughout the ISA application.
Table of Contents
- Features
- Quick Start
- Core Concepts
- API Reference
- Usage Examples
- Styling and Customization
- Testing
- Architecture Notes
Features
- Two label types - Tag and Notice for different semantic contexts
- Three priority levels - High, Medium, and Low for visual hierarchy
- Standalone component - Modern Angular architecture with explicit imports
- Signal-based reactivity - Uses Angular signals for efficient updates
- Type-safe API - TypeScript enums for label type and priority configuration
- CSS class composition - Dynamic class generation based on type and priority
- OnPush change detection - Optimized rendering performance
- ViewEncapsulation.None - Flexible styling with global CSS classes
- Content projection - Full control over label content via ng-content
- Computed classes - Reactive CSS class computation using Angular signals
Quick Start
1. Import the Component
import { Component } from '@angular/core';
import { LabelComponent, Labeltype, LabelPriority } from '@isa/ui/label';
@Component({
selector: 'app-product-status',
template: `
<ui-label [type]="Labeltype.Tag" [priority]="LabelPriority.High">
In Stock
</ui-label>
`,
imports: [LabelComponent]
})
export class ProductStatusComponent {
Labeltype = Labeltype;
LabelPriority = LabelPriority;
}
2. Basic Tag Label
@Component({
template: `
<ui-label [type]="Labeltype.Tag" [priority]="LabelPriority.Medium">
New Arrival
</ui-label>
`,
imports: [LabelComponent]
})
export class ProductBadgeComponent {
Labeltype = Labeltype;
LabelPriority = LabelPriority;
}
3. Notice Label
@Component({
template: `
<ui-label [type]="Labeltype.Notice" [priority]="LabelPriority.High">
Action Required
</ui-label>
`,
imports: [LabelComponent]
})
export class AlertComponent {
Labeltype = Labeltype;
LabelPriority = LabelPriority;
}
Core Concepts
Label Types
The component supports two distinct label types, each with specific semantic meaning:
1. Tag (Default)
- Purpose: Categorization, status indication, metadata display
- Use Cases: Product categories, order status, item tags, filters
- Visual Style: Typically compact, pill-shaped design
- CSS Class:
ui-label__tag
2. Notice
- Purpose: Notifications, alerts, important messages
- Use Cases: Error messages, warnings, system notifications, user alerts
- Visual Style: Typically more prominent, attention-grabbing design
- CSS Class:
ui-label__notice
Priority Levels
Each label type can have one of three priority levels that control visual hierarchy:
1. High Priority (Default)
- Purpose: Critical information, urgent status, primary actions
- Visual Characteristics: Most prominent styling, often with bold colors
- Use Cases: Error states, critical alerts, primary status
- CSS Classes:
ui-label__tag-priority-high(for Tag type)ui-label__notice-priority-high(for Notice type)
2. Medium Priority
- Purpose: Important but non-critical information
- Visual Characteristics: Moderate emphasis, balanced contrast
- Use Cases: Warnings, secondary status, informational notices
- CSS Classes:
ui-label__tag-priority-medium(for Tag type)ui-label__notice-priority-medium(for Notice type)
3. Low Priority
- Purpose: Supplementary information, subtle indicators
- Visual Characteristics: Minimal emphasis, subtle colors
- Use Cases: Hints, optional metadata, background information
- CSS Classes:
ui-label__tag-priority-low(for Tag type)ui-label__notice-priority-low(for Notice type)
Signal-Based Reactivity
The component uses Angular signals for reactive CSS class computation:
// Signal inputs
type = input<Labeltype>(Labeltype.Tag);
priority = input<LabelPriority>(LabelPriority.High);
// Computed signal for type class
typeClass = computed(() => `ui-label__${this.type()}`);
// Computed signal for priority class (combines type and priority)
priorityClass = computed(() => `${this.typeClass()}-priority-${this.priority()}`);
This approach provides:
- Automatic updates - Classes update when inputs change
- Efficient rendering - Only recomputes when dependencies change
- Type safety - TypeScript ensures valid type/priority combinations
CSS Class Structure
The component applies a hierarchical class structure:
ui-label (base class)
├── ui-label__tag (type class for Tag)
│ ├── ui-label__tag-priority-high (Tag + High priority)
│ ├── ui-label__tag-priority-medium (Tag + Medium priority)
│ └── ui-label__tag-priority-low (Tag + Low priority)
└── ui-label__notice (type class for Notice)
├── ui-label__notice-priority-high (Notice + High priority)
├── ui-label__notice-priority-medium (Notice + Medium priority)
└── ui-label__notice-priority-low (Notice + Low priority)
API Reference
LabelComponent
Standalone component for displaying labels with type and priority.
Selector
<ui-label>Content</ui-label>
Inputs
type
- Type:
Labeltype - Default:
Labeltype.Tag - Description: The semantic type of the label
- Values:
Labeltype.Tag- For categorization and statusLabeltype.Notice- For notifications and alerts
Example:
<ui-label [type]="Labeltype.Notice">Important Notice</ui-label>
priority
- Type:
LabelPriority - Default:
LabelPriority.High - Description: The visual priority level of the label
- Values:
LabelPriority.High- Highest visual emphasisLabelPriority.Medium- Moderate visual emphasisLabelPriority.Low- Subtle visual emphasis
Example:
<ui-label [priority]="LabelPriority.Medium">Optional Info</ui-label>
Computed Properties
typeClass()
- Type:
Signal<string> - Returns: CSS class string based on current type
- Format:
ui-label__${type} - Example:
"ui-label__tag"or"ui-label__notice"
priorityClass()
- Type:
Signal<string> - Returns: CSS class string combining type and priority
- Format:
ui-label__${type}-priority-${priority} - Example:
"ui-label__tag-priority-high"
Host Classes
The component automatically applies the following classes to its host element:
['ui-label', typeClass(), priorityClass()]
Example rendered classes:
<ui-label class="ui-label ui-label__tag ui-label__tag-priority-high">
Content
</ui-label>
Type Definitions
Labeltype
TypeScript enum for label type values:
export const Labeltype = {
Tag: 'tag',
Notice: 'notice',
} as const;
export type Labeltype = (typeof Labeltype)[keyof typeof Labeltype];
Usage:
import { Labeltype } from '@isa/ui/label';
// In template
[type]="Labeltype.Tag"
[type]="Labeltype.Notice"
// In component class
readonly labelType = Labeltype.Tag;
LabelPriority
TypeScript enum for priority level values:
export const LabelPriority = {
High: 'high',
Medium: 'medium',
Low: 'low',
} as const;
export type LabelPriority = (typeof LabelPriority)[keyof typeof LabelPriority];
Usage:
import { LabelPriority } from '@isa/ui/label';
// In template
[priority]="LabelPriority.High"
[priority]="LabelPriority.Medium"
[priority]="LabelPriority.Low"
// In component class
readonly priority = LabelPriority.Medium;
Usage Examples
Product Status Tags
import { Component } from '@angular/core';
import { LabelComponent, Labeltype, LabelPriority } from '@isa/ui/label';
@Component({
selector: 'app-product-card',
template: `
<div class="product-card">
<h3>{{ product.name }}</h3>
<!-- In Stock - High priority tag -->
<ui-label
[type]="Labeltype.Tag"
[priority]="LabelPriority.High">
In Stock
</ui-label>
<!-- Category - Medium priority tag -->
<ui-label
[type]="Labeltype.Tag"
[priority]="LabelPriority.Medium">
Electronics
</ui-label>
<!-- Condition - Low priority tag -->
<ui-label
[type]="Labeltype.Tag"
[priority]="LabelPriority.Low">
New
</ui-label>
</div>
`,
imports: [LabelComponent]
})
export class ProductCardComponent {
Labeltype = Labeltype;
LabelPriority = LabelPriority;
product = {
name: 'Wireless Headphones',
inStock: true,
category: 'Electronics'
};
}
Order Status Indicators
import { Component, input, computed } from '@angular/core';
import { LabelComponent, Labeltype, LabelPriority } from '@isa/ui/label';
@Component({
selector: 'app-order-status',
template: `
<ui-label
[type]="Labeltype.Tag"
[priority]="statusPriority()">
{{ statusText() }}
</ui-label>
`,
imports: [LabelComponent]
})
export class OrderStatusComponent {
Labeltype = Labeltype;
status = input.required<'pending' | 'processing' | 'shipped' | 'delivered'>();
statusText = computed(() => {
const statusMap = {
pending: 'Pending',
processing: 'Processing',
shipped: 'Shipped',
delivered: 'Delivered'
};
return statusMap[this.status()];
});
statusPriority = computed(() => {
const priorityMap = {
pending: LabelPriority.High,
processing: LabelPriority.High,
shipped: LabelPriority.Medium,
delivered: LabelPriority.Low
};
return priorityMap[this.status()];
});
}
System Notifications
import { Component, input } from '@angular/core';
import { LabelComponent, Labeltype, LabelPriority } from '@isa/ui/label';
interface Notification {
id: number;
message: string;
type: 'error' | 'warning' | 'info';
}
@Component({
selector: 'app-notification-item',
template: `
<div class="notification">
<ui-label
[type]="Labeltype.Notice"
[priority]="getNotificationPriority()">
{{ getNotificationLabel() }}
</ui-label>
<p>{{ notification().message }}</p>
</div>
`,
imports: [LabelComponent]
})
export class NotificationItemComponent {
Labeltype = Labeltype;
LabelPriority = LabelPriority;
notification = input.required<Notification>();
getNotificationPriority(): LabelPriority {
const type = this.notification().type;
switch (type) {
case 'error':
return LabelPriority.High;
case 'warning':
return LabelPriority.Medium;
case 'info':
return LabelPriority.Low;
}
}
getNotificationLabel(): string {
const type = this.notification().type;
return type.charAt(0).toUpperCase() + type.slice(1);
}
}
Dynamic Labels with NgFor
import { Component } from '@angular/core';
import { LabelComponent, Labeltype, LabelPriority } from '@isa/ui/label';
@Component({
selector: 'app-product-tags',
template: `
<div class="tags-container">
@for (tag of tags; track tag.id) {
<ui-label
[type]="Labeltype.Tag"
[priority]="tag.priority">
{{ tag.label }}
</ui-label>
}
</div>
`,
imports: [LabelComponent]
})
export class ProductTagsComponent {
Labeltype = Labeltype;
tags = [
{ id: 1, label: 'Sale', priority: LabelPriority.High },
{ id: 2, label: 'Free Shipping', priority: LabelPriority.Medium },
{ id: 3, label: 'Eco-Friendly', priority: LabelPriority.Low },
];
}
Conditional Label Display
import { Component, input, computed } from '@angular/core';
import { LabelComponent, Labeltype, LabelPriority } from '@isa/ui/label';
@Component({
selector: 'app-inventory-status',
template: `
@if (showStockLabel()) {
<ui-label
[type]="Labeltype.Tag"
[priority]="stockPriority()">
{{ stockLabel() }}
</ui-label>
}
`,
imports: [LabelComponent]
})
export class InventoryStatusComponent {
Labeltype = Labeltype;
LabelPriority = LabelPriority;
stockQuantity = input.required<number>();
showStockLabel = computed(() => this.stockQuantity() < 10);
stockLabel = computed(() => {
const qty = this.stockQuantity();
if (qty === 0) return 'Out of Stock';
if (qty < 5) return 'Low Stock';
return 'Limited Stock';
});
stockPriority = computed(() => {
const qty = this.stockQuantity();
if (qty === 0) return LabelPriority.High;
if (qty < 5) return LabelPriority.High;
return LabelPriority.Medium;
});
}
Styling and Customization
CSS Class Hierarchy
The component generates a predictable class structure for styling:
/* Base class applied to all labels */
.ui-label {
display: inline-block;
font-family: var(--isa-font-family);
/* Base styles */
}
/* Type-specific base styles */
.ui-label__tag {
/* Tag-specific base styles */
}
.ui-label__notice {
/* Notice-specific base styles */
}
/* Priority-specific styles for Tags */
.ui-label__tag-priority-high {
background-color: var(--isa-accent-red);
color: white;
font-weight: 600;
}
.ui-label__tag-priority-medium {
background-color: var(--isa-accent-yellow);
color: var(--isa-text-primary);
font-weight: 500;
}
.ui-label__tag-priority-low {
background-color: var(--isa-neutral-200);
color: var(--isa-text-secondary);
font-weight: 400;
}
/* Priority-specific styles for Notices */
.ui-label__notice-priority-high {
border-left: 4px solid var(--isa-accent-red);
background-color: var(--isa-accent-red-light);
padding: 0.5rem 1rem;
}
.ui-label__notice-priority-medium {
border-left: 4px solid var(--isa-accent-yellow);
background-color: var(--isa-accent-yellow-light);
padding: 0.5rem 1rem;
}
.ui-label__notice-priority-low {
border-left: 4px solid var(--isa-neutral-300);
background-color: var(--isa-neutral-100);
padding: 0.5rem 1rem;
}
Custom Styling Example
// Override specific label styles
.product-card {
ui-label {
border-radius: 4px;
padding: 0.25rem 0.75rem;
font-size: 0.875rem;
text-transform: uppercase;
letter-spacing: 0.05em;
}
// Custom high-priority tag style
.ui-label__tag-priority-high {
animation: pulse 2s infinite;
}
// Custom notice styles
.ui-label__notice {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
}
Tailwind CSS Integration
The component works seamlessly with Tailwind's design system:
<!-- Using Tailwind utility classes alongside the component -->
<ui-label
[type]="Labeltype.Tag"
[priority]="LabelPriority.High"
class="rounded-full px-3 py-1 text-xs font-semibold">
Featured
</ui-label>
Testing
The library uses Vitest with Angular Testing Utilities for testing.
Running Tests
# Run tests for this library
npx nx test label --skip-nx-cache
# Run tests with coverage
npx nx test label --code-coverage --skip-nx-cache
# Run tests in watch mode
npx nx test label --watch
Test Structure
The library includes comprehensive unit tests covering:
- Type input - Validates correct CSS class generation for each type
- Priority input - Validates correct CSS class generation for each priority
- Default values - Tests default type and priority behavior
- Content projection - Tests ng-content rendering
- Signal reactivity - Tests computed class updates when inputs change
- Class composition - Tests correct combination of base, type, and priority classes
Example Test
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { describe, it, expect, beforeEach } from 'vitest';
import { LabelComponent, Labeltype, LabelPriority } from './label.component';
describe('LabelComponent', () => {
let component: LabelComponent;
let fixture: ComponentFixture<LabelComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [LabelComponent]
});
fixture = TestBed.createComponent(LabelComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should have default type as Tag', () => {
expect(component.type()).toBe(Labeltype.Tag);
});
it('should have default priority as High', () => {
expect(component.priority()).toBe(LabelPriority.High);
});
it('should generate correct type class for Tag', () => {
fixture.componentRef.setInput('type', Labeltype.Tag);
expect(component.typeClass()).toBe('ui-label__tag');
});
it('should generate correct priority class', () => {
fixture.componentRef.setInput('type', Labeltype.Tag);
fixture.componentRef.setInput('priority', LabelPriority.Medium);
expect(component.priorityClass()).toBe('ui-label__tag-priority-medium');
});
it('should apply all classes to host element', () => {
fixture.componentRef.setInput('type', Labeltype.Notice);
fixture.componentRef.setInput('priority', LabelPriority.Low);
fixture.detectChanges();
const element = fixture.nativeElement as HTMLElement;
expect(element.classList.contains('ui-label')).toBe(true);
expect(element.classList.contains('ui-label__notice')).toBe(true);
expect(element.classList.contains('ui-label__notice-priority-low')).toBe(true);
});
});
Architecture Notes
Current Architecture
The library follows Angular standalone component architecture:
LabelComponent (Standalone)
├── Signal Inputs
│ ├── type: Signal<Labeltype>
│ └── priority: Signal<LabelPriority>
├── Computed Signals
│ ├── typeClass(): string
│ └── priorityClass(): string
└── Host Binding
└── [class]: computed classes array
Design Decisions
1. Standalone Component Architecture
Decision: Use standalone component instead of NgModule Rationale:
- Aligns with Angular 20.1.2 best practices
- Reduces boilerplate and complexity
- Improves tree-shaking and bundle optimization
- Enables explicit, localized imports
Impact: Simpler consumption in feature modules
2. Signal-Based Reactivity
Decision: Use input() and computed() signals instead of traditional inputs
Rationale:
- Modern Angular reactivity model
- Automatic dependency tracking
- Better performance with change detection
- Type-safe reactive transformations
Impact: Efficient CSS class updates without manual change detection
3. ViewEncapsulation.None
Decision: Use ViewEncapsulation.None instead of Emulated or ShadowDOM Rationale:
- Enables global styling through BEM-like class naming
- Consistent with ISA design system approach
- Easier integration with Tailwind CSS
- Flexible theming capabilities
Impact: Requires careful CSS class naming to avoid conflicts
4. OnPush Change Detection
Decision: Use OnPush change detection strategy Rationale:
- Optimal performance with signal-based inputs
- Reduces unnecessary change detection cycles
- Signals automatically trigger required updates
- Best practice for presentational components
Impact: Better performance, especially in large lists
5. TypeScript Const Assertions for Enums
Decision: Use const objects with type inference instead of traditional enums Rationale:
- Better type safety with literal types
- Improved autocomplete in IDEs
- Smaller JavaScript bundle size
- More flexible than traditional TypeScript enums
Impact: Type-safe API with minimal runtime overhead
Performance Considerations
- Signal-based inputs - Only recompute classes when inputs actually change
- OnPush change detection - Minimal change detection overhead
- Computed signals - Efficient memoization of class strings
- No template logic - All computation in component class
- Lightweight DOM - Simple ng-content projection with no wrapper elements
Future Enhancements
Potential improvements identified:
- Icon Support - Add optional icon input for visual enhancement
- Dismissible Labels - Add optional close button with output event
- Animation Support - Add entry/exit animations for dynamic labels
- Theme Variants - Support custom color themes via injection tokens
- Size Variants - Add size input (small, medium, large)
- Accessibility Improvements - Add ARIA attributes for screen readers
- Tooltip Integration - Built-in tooltip support for truncated content
Dependencies
Required Libraries
@angular/core- Angular framework (20.1.2)@angular/common- CommonModule for basic directives
Optional Libraries
None - this is a pure presentation component with no external dependencies.
Path Alias
Import from: @isa/ui/label
License
Internal ISA Frontend library - not for external distribution.