@public-ui/components
Version:
Contains all web components that belong to KoliBri - The accessible HTML-Standard.
80 lines (55 loc) • 4.19 kB
Markdown
# TODO: Props-Pattern-Enforcement
## Problem
Wenn Props über `PropsConfigShape` + `ApiFromConfig` in `api.tsx` definiert werden, erzwingt TypeScript nur einen Teil des erforderlichen Boilerplates in der Web-Component-Klasse.
Das **Prop-Dreieck** besteht aus drei Teilen — nur einer davon ist type-erzwungen:
| Teil | Type-erzwungen? | Was passiert bei Fehlen |
| ----------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------ |
| Felddeklaration `_name!: string` | **Ja** — via `ComponentProps<T>` in `WebComponentInterface` | compile error |
| `@Prop()` Decorator | **Nein** | kein Attribut-Binding, silent runtime bug |
| `@Watch('_name')` Decorator | **Nein** (`watchName()` Methode wird erzwungen, aber nicht der Decorator) | Stencil ruft Methode nie auf, silent runtime bug |
| Forwarding in `componentWillLoad()` | **Nein** | Prop-Wert kommt nie im Controller an |
## Aktueller Stand
`implements WebComponentInterface<SkeletonApi>` ist korrekt und schon vorhanden — es deckt alles ab, was TypeScript abdecken _kann_. Die Lücke ist Stencil-spezifisch: Stencil-Decorators sind reine Metadaten und für das TypeScript-Typsystem unsichtbar.
## Lösungsoptionen
### Option A: Custom ESLint-Regel (empfohlen, kurzfristig)
Eine AST-basierte Regel mit `@typescript-eslint`, die prüft:
> Jedes Feld einer Klasse, die `implements WebComponentInterface<...>` trägt und dem Muster `_${name}` entspricht, muss `@Prop()` und ein `@Watch('_${name}')` besitzen.
- Machbar ohne Änderungen am Typsystem
- Greift in CI/CD und im Editor
- Schließt die Lücke für bestehende und neue Komponenten
### Option B: Code-Generator für Prop-Dreieck-Boilerplate (empfohlen, langfristig)
Ein Script/Tool, das aus `propsConfig` automatisch den WebComponent-Boilerplate erzeugt:
```ts
// aus: skeletonPropsConfig.required = [nameProp]
// wird generiert:
@Prop() public _name!: string;
@Watch('_name')
watchName(value?: string): void {
this.ctrl.watchName(value);
}
// in componentWillLoad():
// name: this._name,
```
- Eliminiert die Fehlerquelle bei neuen Props vollständig
- Kein Scaffolding-Tool existiert aktuell im Projekt
### Option C: JSDoc-Warnung auf `WebComponentInterface<T>` (minimal, sofort)
```ts
/**
* @remarks
* TypeScript erzwingt Felddeklarationen und Watcher-Methodensignaturen.
* NICHT erzwungen werden: @Prop() Decorator, @Watch() Decorator, componentWillLoad() Forwarding.
* Alle drei Teile des Prop-Dreiecks müssen manuell korrekt implementiert werden.
*/
export type WebComponentInterface<T extends ComponentApi> = ...
```
## Offene Punkte (Needs Deeper Look)
- [ ] **Custom ESLint-Regel implementieren** für `@Prop()` + `@Watch()` Enforcement
- [ ] **Code-Generator** für Prop-Dreieck-Boilerplate aus `propsConfig`
- [ ] **`label` State-Disconnect im Skeleton** beheben — `@State label = 'Label'` im WebComponent und `'Click me'` im Controller sind unverbunden
- [ ] **`@Watch('_name')` Typo-Risiko** — `@Watch('_nane')` wäre kein TypeScript-Fehler; ESLint-Regel würde das mit abdecken
- [ ] **Alle Komponenten auditen** (`avatar`, `click-button`, `icon`, `image`, `progress`, `quote`) auf Prop-Dreieck-Vollständigkeit
- [ ] **A11y-Tests** für Skeleton hinzufügen — kein `*.e2e.ts` / `*.spec.ts` vorhanden
- [ ] **`event.preventDefault()` in `ClickButtonFC`** prüfen — blockiert alle Keyboard-Events (NVDA/JAWS-Test nötig)
- [ ] **JSDoc `@remarks`** auf `WebComponentInterface<T>` in `generic-types.ts` ergänzen
## Accessibility-Risiko
Bei fehlendem Prop-Wiring entsteht konkret eine WCAG-4.1.2-Verletzung (Name, Role, Value): Buttons erhalten keinen accessible name oder einen falschen Fallback-Text. `axe-core` würde dies in e2e-Tests fangen — aber nur wenn solche Tests existieren.