- 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
14 KiB
@isa/ui/toolbar
A flexible toolbar container component for Angular applications with configurable sizing and content projection.
Overview
The Toolbar library provides a single component (ui-toolbar) that serves as a styled container for toolbar content. It supports two size variants (small and medium) and uses content projection to allow flexible composition of toolbar items such as buttons, inputs, and action controls.
Table of Contents
- Features
- Quick Start
- Component API
- Usage Examples
- Styling and Customization
- Architecture Notes
- Testing
- Dependencies
Features
- Two size variants - Small and medium toolbar heights
- Content projection - Full flexibility for toolbar content composition
- ViewEncapsulation.None - Allows external styling
- OnPush change detection - Optimized performance
- Signal-based sizing - Reactive size computation with
computed() - Host class bindings - Dynamic CSS class application
- Standalone component - Modern Angular architecture
Quick Start
1. Import Component
import { Component } from '@angular/core';
import { ToolbarComponent } from '@isa/ui/toolbar';
@Component({
selector: 'app-page-header',
imports: [ToolbarComponent],
template: `...`
})
export class PageHeaderComponent {}
2. Basic Usage
<ui-toolbar>
<h1>Page Title</h1>
<button>Action</button>
</ui-toolbar>
3. Size Variants
<!-- Medium toolbar (default) -->
<ui-toolbar>
<span>Default medium toolbar</span>
</ui-toolbar>
<!-- Small toolbar -->
<ui-toolbar [size]="'small'">
<span>Compact toolbar</span>
</ui-toolbar>
Component API
ToolbarComponent
Container component for toolbar content with size variants.
Selector
'ui-toolbar'
Inputs
| Input | Type | Default | Description |
|---|---|---|---|
size |
ToolbarSize |
'medium' |
Size variant of the toolbar |
Size Options
export const ToolbarSize = {
Small: 'small',
Medium: 'medium',
} as const;
export type ToolbarSize = (typeof ToolbarSize)[keyof typeof ToolbarSize];
Computed Properties
| Property | Type | Description |
|---|---|---|
sizeClass() |
string |
Computed CSS class based on size (ui-toolbar__small or ui-toolbar__medium) |
Host Bindings
class:['ui-toolbar', sizeClass()]- Base class and size-specific class
Template
<ng-content></ng-content>
Encapsulation
- Uses
ViewEncapsulation.Noneto allow external styling of toolbar content
Usage Examples
Page Header Toolbar
import { Component } from '@angular/core';
import { ToolbarComponent } from '@isa/ui/toolbar';
import { ButtonComponent } from '@isa/ui/buttons';
@Component({
selector: 'app-page-header',
imports: [ToolbarComponent, ButtonComponent],
template: `
<ui-toolbar>
<h1 class="page-title">Dashboard</h1>
<div class="toolbar-actions">
<ui-button (click)="refresh()">Refresh</ui-button>
<ui-button appearance="accent" (click)="create()">Create</ui-button>
</div>
</ui-toolbar>
`,
styles: [`
.page-title {
flex: 1;
margin: 0;
}
.toolbar-actions {
display: flex;
gap: 0.5rem;
}
`]
})
export class PageHeaderComponent {
refresh() { /* ... */ }
create() { /* ... */ }
}
Search Toolbar
import { Component } from '@angular/core';
import { ToolbarComponent, ToolbarSize } from '@isa/ui/toolbar';
import { TextFieldComponent, InputControlDirective } from '@isa/ui/input-controls';
@Component({
selector: 'app-search-toolbar',
imports: [ToolbarComponent, TextFieldComponent, InputControlDirective],
template: `
<ui-toolbar [size]="ToolbarSize.Small">
<ui-text-field>
<input uiInputControl type="text" placeholder="Search..." />
</ui-text-field>
<button (click)="search()">Search</button>
</ui-toolbar>
`
})
export class SearchToolbarComponent {
readonly ToolbarSize = ToolbarSize;
search() { /* ... */ }
}
Filter Toolbar with Multiple Controls
import { Component, signal } from '@angular/core';
import { ToolbarComponent } from '@isa/ui/toolbar';
import { DropdownButtonComponent, DropdownOptionComponent } from '@isa/ui/input-controls';
@Component({
selector: 'app-filter-toolbar',
imports: [ToolbarComponent, DropdownButtonComponent, DropdownOptionComponent],
template: `
<ui-toolbar>
<span class="filter-label">Filters:</span>
<ui-dropdown [(value)]="selectedStatus" label="Status">
<ui-dropdown-option [value]="'all'">All</ui-dropdown-option>
<ui-dropdown-option [value]="'active'">Active</ui-dropdown-option>
<ui-dropdown-option [value]="'inactive'">Inactive</ui-dropdown-option>
</ui-dropdown>
<ui-dropdown [(value)]="selectedCategory" label="Category">
<ui-dropdown-option [value]="'all'">All</ui-dropdown-option>
<ui-dropdown-option [value]="'electronics'">Electronics</ui-dropdown-option>
<ui-dropdown-option [value]="'books'">Books</ui-dropdown-option>
</ui-dropdown>
<button (click)="resetFilters()">Reset</button>
</ui-toolbar>
`,
styles: [`
.filter-label {
font-weight: 600;
margin-right: 0.5rem;
}
`]
})
export class FilterToolbarComponent {
selectedStatus = signal('all');
selectedCategory = signal('all');
resetFilters() {
this.selectedStatus.set('all');
this.selectedCategory.set('all');
}
}
Dynamic Size Toolbar
import { Component, signal } from '@angular/core';
import { ToolbarComponent, ToolbarSize } from '@isa/ui/toolbar';
@Component({
selector: 'app-dynamic-toolbar',
imports: [ToolbarComponent],
template: `
<ui-toolbar [size]="currentSize()">
<span>Toolbar Content</span>
<button (click)="toggleSize()">Toggle Size</button>
</ui-toolbar>
`
})
export class DynamicToolbarComponent {
currentSize = signal<ToolbarSize>(ToolbarSize.Medium);
toggleSize() {
const newSize = this.currentSize() === ToolbarSize.Medium
? ToolbarSize.Small
: ToolbarSize.Medium;
this.currentSize.set(newSize);
}
}
Toolbar with Icons and Actions
import { Component } from '@angular/core';
import { ToolbarComponent } from '@isa/ui/toolbar';
import { NgIcon } from '@ng-icons/core';
import { ButtonComponent } from '@isa/ui/buttons';
@Component({
selector: 'app-actions-toolbar',
imports: [ToolbarComponent, ButtonComponent, NgIcon],
template: `
<ui-toolbar>
<div class="toolbar-section">
<ng-icon name="isaActionMenu" size="1.5rem"></ng-icon>
<span class="toolbar-title">Orders</span>
</div>
<div class="toolbar-section">
<ui-button (click)="export()">
<ng-icon name="isaActionDownload"></ng-icon>
Export
</ui-button>
<ui-button appearance="accent" (click)="createOrder()">
<ng-icon name="isaActionPlus"></ng-icon>
New Order
</ui-button>
</div>
</ui-toolbar>
`,
styles: [`
.toolbar-section {
display: flex;
align-items: center;
gap: 0.75rem;
}
.toolbar-title {
font-size: 1.25rem;
font-weight: 600;
}
`]
})
export class ActionsToolbarComponent {
export() { /* ... */ }
createOrder() { /* ... */ }
}
Responsive Toolbar with Breakpoints
import { Component, computed } from '@angular/core';
import { ToolbarComponent, ToolbarSize } from '@isa/ui/toolbar';
import { breakpoint, Breakpoint } from '@isa/ui/layout';
@Component({
selector: 'app-responsive-toolbar',
imports: [ToolbarComponent],
template: `
<ui-toolbar [size]="toolbarSize()">
@if (isDesktop()) {
<h1>Full Desktop Title</h1>
<div class="actions">
<button>Action 1</button>
<button>Action 2</button>
<button>Action 3</button>
</div>
} @else {
<h1>Mobile</h1>
<button>Menu</button>
}
</ui-toolbar>
`
})
export class ResponsiveToolbarComponent {
isDesktop = breakpoint([Breakpoint.Desktop, Breakpoint.DekstopL, Breakpoint.DekstopXL]);
toolbarSize = computed(() =>
this.isDesktop() ? ToolbarSize.Medium : ToolbarSize.Small
);
}
Styling and Customization
CSS Classes
The component exposes the following CSS classes for styling:
/* Base toolbar class */
.ui-toolbar {
/* Base toolbar styles */
}
/* Medium toolbar (default) */
.ui-toolbar__medium {
/* Medium size styles */
}
/* Small toolbar */
.ui-toolbar__small {
/* Small size styles */
}
Example Styling
// Base toolbar styling
.ui-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 1rem;
background-color: var(--isa-surface-100);
border-bottom: 1px solid var(--isa-border-200);
// Medium toolbar (default height)
&.ui-toolbar__medium {
height: 4rem;
padding: 0 1.5rem;
}
// Small toolbar (compact height)
&.ui-toolbar__small {
height: 3rem;
padding: 0 1rem;
font-size: 0.875rem;
}
}
// Toolbar sections and layouts
.ui-toolbar {
.toolbar-section {
display: flex;
align-items: center;
gap: 0.5rem;
}
.toolbar-title {
font-weight: 600;
font-size: 1.125rem;
}
.toolbar-actions {
display: flex;
gap: 0.5rem;
margin-left: auto;
}
}
Dark Theme Example
// Dark mode toolbar
.dark-theme {
.ui-toolbar {
background-color: var(--isa-surface-900);
border-bottom-color: var(--isa-border-700);
color: var(--isa-text-inverse);
}
}
Architecture Notes
Size Management
The component uses Angular signals for reactive size management:
size = input<ToolbarSize>('medium');
sizeClass = computed(() => `ui-toolbar__${this.size()}`);
Benefits:
- Automatic updates - Size changes trigger class recomputation
- Type safety - ToolbarSize type ensures valid values
- Performance - OnPush detection with signals
ViewEncapsulation.None
The component uses ViewEncapsulation.None to allow external styling:
@Component({
encapsulation: ViewEncapsulation.None
})
This enables:
- Parent components can style toolbar content
- Global styles can target toolbar elements
- Flexible theme integration
Content Projection
The toolbar uses simple content projection for maximum flexibility:
<ng-content></ng-content>
This allows any content structure:
- Buttons, inputs, dropdowns
- Custom components
- Icons and text
- Complex layouts
Standalone Architecture
The component is fully standalone with no dependencies:
@Component({
standalone: true,
imports: []
})
Testing
The library uses Jest for testing.
Running Tests
# Run tests for this library
npx nx test ui-toolbar --skip-nx-cache
# Run tests with coverage
npx nx test ui-toolbar --code-coverage --skip-nx-cache
# Run tests in watch mode
npx nx test ui-toolbar --watch
Test Coverage
The library should include tests covering:
- Component rendering - Verifies toolbar renders correctly
- Size variants - Tests both small and medium sizes
- Content projection - Validates content appears correctly
- Class bindings - Tests dynamic class application
- Signal reactivity - Tests size changes update classes
Example Test
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ToolbarComponent, ToolbarSize } from './toolbar.component';
describe('ToolbarComponent', () => {
let component: ToolbarComponent;
let fixture: ComponentFixture<ToolbarComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ToolbarComponent]
});
fixture = TestBed.createComponent(ToolbarComponent);
component = fixture.componentInstance;
});
it('should apply medium size by default', () => {
fixture.detectChanges();
expect(component.sizeClass()).toBe('ui-toolbar__medium');
});
it('should apply small size when specified', () => {
component.size = ToolbarSize.Small;
fixture.detectChanges();
expect(component.sizeClass()).toBe('ui-toolbar__small');
});
it('should project content correctly', () => {
const compiled = fixture.nativeElement;
compiled.innerHTML = '<span>Test Content</span>';
fixture.detectChanges();
expect(compiled.textContent).toContain('Test Content');
});
});
Dependencies
Required Libraries
@angular/core- Angular framework (v20.1.2)
Path Alias
Import from: @isa/ui/toolbar
No External Dependencies
This component has no external dependencies beyond Angular core, making it lightweight and easy to integrate.
Best Practices
- Consistent Sizing - Use the same toolbar size throughout similar contexts (e.g., all page headers use medium)
- Flexbox Layouts - Structure toolbar content using flexbox for responsive behavior
- Semantic HTML - Use appropriate HTML elements within the toolbar (headings, buttons, etc.)
- Accessibility - Ensure toolbar controls are keyboard accessible and have proper ARIA labels
- Content Organization - Group related toolbar items visually using spacing and dividers
- Responsive Design - Consider using the breakpoint service to adapt toolbar content for different screen sizes
Common Patterns
Left-Right Layout
<ui-toolbar>
<div class="toolbar-left">
<h1>Title</h1>
<span>Subtitle</span>
</div>
<div class="toolbar-right">
<button>Action 1</button>
<button>Action 2</button>
</div>
</ui-toolbar>
<style>
.ui-toolbar {
display: flex;
justify-content: space-between;
}
</style>
Three-Section Layout
<ui-toolbar>
<div class="toolbar-left">Left content</div>
<div class="toolbar-center">Center content</div>
<div class="toolbar-right">Right content</div>
</ui-toolbar>
<style>
.ui-toolbar {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 1rem;
}
.toolbar-center {
justify-self: center;
}
.toolbar-right {
justify-self: end;
}
</style>
License
Internal ISA Frontend library - not for external distribution.