Merged PR 1841: feat(ui-input-controls, oms-return-process): introduce text field container,...

feat(ui-input-controls, oms-return-process): introduce text field container, clear, and errors components

- Add `ui-text-field-container`, `ui-text-field-clear`, and `ui-text-field-errors` as standalone components for improved text field composition and error handling.
- Update SCSS to include new styles for container, clear, and errors components, ensuring visual consistency and error highlighting.
- Refactor `ReturnProcessProductQuestionComponent` to use the new containerized text field structure, improving template clarity and error display.
- Update Storybook story for `TextField` to demonstrate new composition and error handling.
- Export new components from the input-controls public API for external usage.

Ref: #4989, #5058
This commit is contained in:
Nino Righi
2025-06-05 17:12:28 +00:00
committed by Lorenz Hilpert
parent f5507a874c
commit b261273228
28 changed files with 597 additions and 91 deletions

View File

@@ -7,7 +7,10 @@ export * from './lib/dropdown/dropdown.component';
export * from './lib/dropdown/dropdown.types';
export * from './lib/listbox/listbox-item.directive';
export * from './lib/listbox/listbox.directive';
export * from './lib/text-field/text-field.component';
export * from './lib/text-field/textarea.component';
export * from './lib/text-field/text-field.component';
export * from './lib/text-field/text-field-clear.component';
export * from './lib/text-field/text-field-container.component';
export * from './lib/text-field/text-field-errors.component';
export * from './lib/chips/chips.component';
export * from './lib/chips/chip-option.component';

View File

@@ -1,7 +1,10 @@
@use "./lib/checkbox/checkbox";
@use "./lib/checkbox/checklist";
@use "./lib/chips/chips";
@use "./lib/dropdown/dropdown";
@use "./lib/listbox/listbox";
@use "./lib/text-field/text-field";
@use "./lib/text-field/textarea";
@use './lib/checkbox/checkbox';
@use './lib/checkbox/checklist';
@use './lib/chips/chips';
@use './lib/dropdown/dropdown';
@use './lib/listbox/listbox';
@use './lib/text-field/text-field';
@use './lib/text-field/text-field-clear';
@use './lib/text-field/text-field-container';
@use './lib/text-field/text-field-errors';
@use './lib/text-field/textarea';

View File

@@ -1,11 +1,15 @@
import { Directive, inject, input, OnInit } from '@angular/core';
import { Directive, inject, model, output } from '@angular/core';
import { NgControl } from '@angular/forms';
@Directive({ selector: 'input[uiInputControl]', host: { class: 'ui-input-control' } })
@Directive({
selector: 'input[uiInputControl]',
host: { class: 'ui-input-control' },
})
export class InputControlDirective<T> {
readonly control = inject(NgControl, { optional: true, self: true });
readonly value = input<T>();
readonly value = model<T>();
cleared = output<void>();
getValue(): T | undefined {
if (this.control) {
@@ -14,4 +18,13 @@ export class InputControlDirective<T> {
return this.value();
}
clear(): void {
if (this.control) {
this.control.reset();
} else {
this.value.set(undefined);
}
this.cleared.emit();
}
}

View File

@@ -0,0 +1,5 @@
.ui-text-field-clear {
ui-icon-button {
@apply text-isa-neutral-900;
}
}

View File

@@ -0,0 +1,9 @@
.ui-text-field-container {
@apply flex flex-col gap-1 items-start justify-center;
&:has(.ui-text-field-errors > *) {
.ui-text-field {
@apply border-isa-accent-red;
}
}
}

View File

@@ -0,0 +1,3 @@
.ui-text-field-errors {
@apply flex flex-col text-isa-accent-red isa-text-caption-regular;
}

View File

@@ -0,0 +1,7 @@
<ui-icon-button
type="button"
[color]="'neutral'"
[size]="size()"
(click)="hostComponent.inputControl().clear()"
name="isaActionClose"
></ui-icon-button>

View File

@@ -0,0 +1,31 @@
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
} from '@angular/core';
import { TextFieldComponent } from './text-field.component';
import { provideIcons } from '@ng-icons/core';
import { isaActionClose } from '@isa/icons';
import { IconButtonComponent } from '@isa/ui/buttons';
@Component({
selector: 'ui-text-field-clear',
templateUrl: './text-field-clear.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
host: {
'[class]': '["ui-text-field-clear", sizeClass()]',
},
providers: [provideIcons({ isaActionClose })],
imports: [IconButtonComponent],
})
export class TextFieldClearComponent {
hostComponent = inject(TextFieldComponent, { host: true });
size = this.hostComponent.size;
sizeClass = computed(() => {
return `ui-text-field-clear__${this.size()}`;
});
}

View File

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

View File

@@ -0,0 +1,12 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'ui-text-field-container',
templateUrl: './text-field-container.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
host: {
'[class]': '["ui-text-field-container"]',
},
})
export class TextFieldContainerComponent {}

View File

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

View File

@@ -0,0 +1,12 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'ui-text-field-errors',
templateUrl: './text-field-errors.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
host: {
'[class]': '["ui-text-field-errors"]',
},
})
export class TextFieldErrorsComponent {}

View File

@@ -1,4 +1,10 @@
import { ChangeDetectionStrategy, Component, computed, contentChild, input } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
computed,
contentChild,
input,
} from '@angular/core';
import { InputControlDirective } from '../core/input-control.directive';
export const TextFieldSize = {