Merged PR 2055: feature(ui-label, ahf, warenausgabe, customer-orders): Added and Updated Labe...

feature(ui-label, ahf, warenausgabe, customer-orders): Added and Updated Label Library and Label to the Views, Updated Positioning

Ref: #5479
This commit is contained in:
Nino Righi
2025-11-28 12:37:11 +00:00
committed by Lorenz Hilpert
parent a5bb8b2895
commit 41630d5d7c
47 changed files with 1830 additions and 1549 deletions

83
libs/ui/notice/README.md Normal file
View File

@@ -0,0 +1,83 @@
# @isa/ui/notice
A notice component for displaying prominent notifications and alerts with configurable priority levels.
## Installation
```typescript
import { NoticeComponent, NoticePriority } from '@isa/ui/notice';
```
## Component
### NoticeComponent
**Figma:** [ISA Design System - Notice](https://www.figma.com/design/bK0IW6akzSjHxmMwQfVPRW/ISA-DESIGN-SYSTEM?node-id=2551-4407&m=dev)
## Priority Levels
| Priority | Description | Background |
| -------- | ---------------- | ---------------- |
| `high` | Most prominent | Secondary color |
| `medium` | Moderate | Neutral color |
| `low` | Subtle (no fill) | Transparent |
## Usage
```html
<!-- High priority (default) -->
<ui-notice>Action Required</ui-notice>
<!-- Medium priority -->
<ui-notice [priority]="NoticePriority.Medium">Secondary message</ui-notice>
<!-- Low priority -->
<ui-notice priority="low">Info message</ui-notice>
```
### Component Example
```typescript
import { Component } from '@angular/core';
import { NoticeComponent, NoticePriority } from '@isa/ui/notice';
@Component({
selector: 'app-alert',
template: `
<ui-notice [priority]="NoticePriority.High">Action Required</ui-notice>
<ui-notice [priority]="NoticePriority.Medium">Limited Stock</ui-notice>
`,
imports: [NoticeComponent],
})
export class AlertComponent {
NoticePriority = NoticePriority;
}
```
## API
| Input | Type | Default | Description |
| ---------- | ---------------- | -------------------- | --------------------------------- |
| `priority` | `NoticePriority` | `NoticePriority.High`| Visual priority level |
## CSS Classes
- `.ui-notice` - Base class
- `.ui-notice--high` - High priority (secondary background)
- `.ui-notice--medium` - Medium priority (neutral background)
- `.ui-notice--low` - Low priority (transparent)
## Accessibility
- `role="status"` - Indicates status information
- E2E testing attributes (`data-what`, `data-which`)
## E2E Testing
```typescript
// Select all notices
page.locator('[data-what="notice"]');
// Select specific priority
page.locator('[data-what="notice"][data-which="priority-high"]');
```

View File

@@ -0,0 +1,34 @@
const nx = require('@nx/eslint-plugin');
const baseConfig = require('../../../eslint.config.js');
module.exports = [
...baseConfig,
...nx.configs['flat/angular'],
...nx.configs['flat/angular-template'],
{
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'ui',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'ui',
style: 'kebab-case',
},
],
},
},
{
files: ['**/*.html'],
// Override or add rules here
rules: {},
},
];

View File

@@ -0,0 +1,20 @@
{
"name": "ui-notice",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/ui/notice/src",
"prefix": "ui",
"projectType": "library",
"tags": ["scope:ui", "type:ui"],
"targets": {
"test": {
"executor": "@nx/vite:test",
"outputs": ["{options.reportsDirectory}"],
"options": {
"reportsDirectory": "../../../coverage/libs/ui/notice"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}

View File

@@ -0,0 +1,2 @@
export * from './lib/notice/notice.component';
export * from './lib/notice/types';

View File

@@ -0,0 +1,16 @@
.ui-notice {
@apply inline-flex flex-col items-start;
@apply p-2 min-w-48 rounded-lg isa-text-body-2-bold text-isa-neutral-900;
}
.ui-notice--high {
@apply bg-isa-secondary-100;
}
.ui-notice--medium {
@apply bg-isa-neutral-100;
}
.ui-notice--low {
@apply bg-transparent;
}

View File

@@ -0,0 +1 @@
<ng-content></ng-content>

View File

@@ -0,0 +1,128 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NoticeComponent } from './notice.component';
import { NoticePriority } from './types';
describe('NoticeComponent', () => {
let component: NoticeComponent;
let fixture: ComponentFixture<NoticeComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [NoticeComponent],
}).compileComponents();
fixture = TestBed.createComponent(NoticeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
describe('Component Setup and Initialization', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
it('should have default priority as high', () => {
expect(component.priority()).toBe(NoticePriority.High);
});
it('should accept medium priority', () => {
fixture.componentRef.setInput('priority', NoticePriority.Medium);
fixture.detectChanges();
expect(component.priority()).toBe(NoticePriority.Medium);
});
it('should accept low priority', () => {
fixture.componentRef.setInput('priority', NoticePriority.Low);
fixture.detectChanges();
expect(component.priority()).toBe(NoticePriority.Low);
});
it('should have correct CSS class for high priority', () => {
expect(component.priorityClass()).toBe('ui-notice--high');
});
it('should have correct CSS class for medium priority', () => {
fixture.componentRef.setInput('priority', NoticePriority.Medium);
fixture.detectChanges();
expect(component.priorityClass()).toBe('ui-notice--medium');
});
it('should have correct CSS class for low priority', () => {
fixture.componentRef.setInput('priority', NoticePriority.Low);
fixture.detectChanges();
expect(component.priorityClass()).toBe('ui-notice--low');
});
});
describe('Template Rendering', () => {
it('should display content with high priority classes', () => {
const hostElement = fixture.debugElement.nativeElement;
expect(hostElement.classList.contains('ui-notice')).toBe(true);
expect(hostElement.classList.contains('ui-notice--high')).toBe(true);
});
it('should display content with medium priority classes', () => {
fixture.componentRef.setInput('priority', NoticePriority.Medium);
fixture.detectChanges();
const hostElement = fixture.debugElement.nativeElement;
expect(hostElement.classList.contains('ui-notice')).toBe(true);
expect(hostElement.classList.contains('ui-notice--medium')).toBe(true);
});
it('should display content with low priority classes', () => {
fixture.componentRef.setInput('priority', NoticePriority.Low);
fixture.detectChanges();
const hostElement = fixture.debugElement.nativeElement;
expect(hostElement.classList.contains('ui-notice')).toBe(true);
expect(hostElement.classList.contains('ui-notice--low')).toBe(true);
});
it('should update classes when priority changes', () => {
const hostElement = fixture.debugElement.nativeElement;
expect(hostElement.classList.contains('ui-notice--high')).toBe(true);
expect(hostElement.classList.contains('ui-notice--medium')).toBe(false);
fixture.componentRef.setInput('priority', NoticePriority.Medium);
fixture.detectChanges();
expect(hostElement.classList.contains('ui-notice--high')).toBe(false);
expect(hostElement.classList.contains('ui-notice--medium')).toBe(true);
});
});
describe('Component Structure', () => {
it('should have proper host class binding', () => {
const hostElement = fixture.debugElement.nativeElement;
expect(hostElement.classList.contains('ui-notice')).toBe(true);
});
it('should have E2E testing attributes', () => {
const hostElement = fixture.debugElement.nativeElement;
expect(hostElement.getAttribute('data-what')).toBe('notice');
expect(hostElement.getAttribute('data-which')).toBe('priority-high');
});
it('should have correct data-which for different priorities', () => {
const hostElement = fixture.debugElement.nativeElement;
expect(hostElement.getAttribute('data-which')).toBe('priority-high');
fixture.componentRef.setInput('priority', NoticePriority.Medium);
fixture.detectChanges();
expect(hostElement.getAttribute('data-which')).toBe('priority-medium');
fixture.componentRef.setInput('priority', NoticePriority.Low);
fixture.detectChanges();
expect(hostElement.getAttribute('data-which')).toBe('priority-low');
});
it('should have accessibility role', () => {
const hostElement = fixture.debugElement.nativeElement;
expect(hostElement.getAttribute('role')).toBe('status');
});
});
});

View File

@@ -0,0 +1,41 @@
import {
ChangeDetectionStrategy,
Component,
computed,
input,
ViewEncapsulation,
} from '@angular/core';
import { NoticePriority } from './types';
/**
* Notice component for displaying prominent notifications and alerts.
* Supports high, medium, and low priority variants.
*
* @example
* ```html
* <ui-notice>Important message</ui-notice>
* <ui-notice [priority]="NoticePriority.Medium">Secondary message</ui-notice>
* <ui-notice [priority]="NoticePriority.Low">Info message</ui-notice>
* ```
*
* @see https://www.figma.com/design/bK0IW6akzSjHxmMwQfVPRW/ISA-DESIGN-SYSTEM?node-id=2551-4407&m=dev
*/
@Component({
selector: 'ui-notice',
template: '<ng-content></ng-content>',
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'[class]': '["ui-notice", priorityClass()]',
'data-what': 'notice',
'[attr.data-which]': '"priority-" + priority()',
'role': 'status',
},
})
export class NoticeComponent {
/** The priority level of the notice (high, medium, low). */
priority = input<NoticePriority>(NoticePriority.High);
/** A computed CSS class based on the current priority. */
priorityClass = computed(() => `ui-notice--${this.priority()}`);
}

View File

@@ -0,0 +1,7 @@
export const NoticePriority = {
High: 'high',
Medium: 'medium',
Low: 'low',
} as const;
export type NoticePriority = (typeof NoticePriority)[keyof typeof NoticePriority];

View File

@@ -0,0 +1 @@
@use "lib/notice/notice";

View File

@@ -0,0 +1,13 @@
import '@angular/compiler';
import '@analogjs/vitest-angular/setup-zone';
import {
BrowserTestingModule,
platformBrowserTesting,
} from '@angular/platform-browser/testing';
import { getTestBed } from '@angular/core/testing';
getTestBed().initTestEnvironment(
BrowserTestingModule,
platformBrowserTesting(),
);

View File

@@ -0,0 +1,30 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"importHelpers": true,
"moduleResolution": "bundler",
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"module": "preserve"
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"typeCheckHostBindings": true,
"strictTemplates": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}

View File

@@ -0,0 +1,27 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": []
},
"exclude": [
"src/**/*.spec.ts",
"src/test-setup.ts",
"jest.config.ts",
"src/**/*.test.ts",
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx"
],
"include": ["src/**/*.ts"]
}

View File

@@ -0,0 +1,29 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"types": [
"vitest/globals",
"vitest/importMeta",
"vite/client",
"node",
"vitest"
]
},
"include": [
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"src/**/*.d.ts"
],
"files": ["src/test-setup.ts"]
}

View File

@@ -0,0 +1,29 @@
/// <reference types='vitest' />
import { defineConfig } from 'vite';
import angular from '@analogjs/vite-plugin-angular';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
export default
// @ts-expect-error - Vitest reporter tuple types have complex inference issues
defineConfig(() => ({
root: __dirname,
cacheDir: '../../../node_modules/.vite/libs/ui/notice',
plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
test: {
watch: false,
globals: true,
environment: 'jsdom',
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
setupFiles: ['src/test-setup.ts'],
reporters: [
'default',
['junit', { outputFile: '../../../testresults/junit-ui-notice.xml' }],
],
coverage: {
reportsDirectory: '../../../coverage/libs/ui/notice',
provider: 'v8' as const,
reporter: ['text', 'cobertura'],
},
},
}));