# @ocelot-social/ui - Projektdokumentation > Dieses Dokument dient als zentrale Planungs- und Statusübersicht für das UI-Library Subprojekt. > Es ermöglicht das Pausieren und Wiederaufnehmen der Arbeit zu jedem Zeitpunkt. --- ## Inhaltsverzeichnis ### Schnellzugriff (Status) | Abschnitt | Beschreibung | |-----------|--------------| | [Fortschritt](#fortschritt) | Visuelle Fortschrittsanzeige | | [Aktueller Stand](#aktueller-stand) | Was zuletzt erledigt wurde | | [Meilensteine](#meilensteine) | Phasen 0-5 mit Checklisten | ### Nach Thema **[VISION](#vision)** | # | Abschnitt | |---|-----------| | 1 | [Projektziel & Vision](#1-projektziel--vision) | **[TECHNISCHE GRUNDLAGEN](#technische-grundlagen)** | # | Abschnitt | |---|-----------| | 2 | [Tech-Stack](#2-tech-stack) | | 3 | [Build & Distribution](#3-build--distribution) | | 4 | [Icon-Architektur](#4-icon-architektur) | | 5 | [Design-Token & Branding](#5-design-token--branding) | **[PROZESSE & QUALITÄT](#prozesse--qualität)** | # | Abschnitt | |---|-----------| | 6 | [CI/CD & Release](#6-cicd--release) | | 7 | [Dokumentation & DX](#7-dokumentation--dx) | | 8 | [Migrationsstrategie](#8-migrationsstrategie) | | 9 | [Dokumentationsstrategie](#9-dokumentationsstrategie) | | 10 | [Kompatibilitätstests](#10-kompatibilitätstests) | **[REFERENZ & HISTORIE](#referenz--historie)** | # | Abschnitt | |---|-----------| | 11 | [Entscheidungen](#11-entscheidungen) | | 12 | [Arbeitsprotokoll](#12-arbeitsprotokoll) | | 13 | [Komponenten-Katalog](#13-komponenten-katalog) | | 14 | [Ressourcen & Links](#14-ressourcen--links) | | 15 | [Dokumentationsstrategie (Details)](#15-dokumentationsstrategie-details) | **[ABGRENZUNGEN](#abgrenzungen)** | # | Abschnitt | |---|-----------| | 16 | [Library vs. Webapp](#16-library-vs-webapp) | | 16a | [Webapp ↔ Maintenance Code-Sharing](#16a-webapp--maintenance-code-sharing) | | 16b | [Daten-Entkopplung (ViewModel/Mapper)](#16b-daten-entkopplung-viewmodelmapper-pattern) | | 17 | [Externe Abhängigkeiten](#17-externe-abhängigkeiten) | | 18 | [Kompatibilitätstests (Details)](#18-kompatibilitätstests-details) | | 19 | [Komplexitätsanalyse](#19-komplexitätsanalyse) | --- ### Wie dieses Dokument verwendet wird **Zum Fortsetzen der Arbeit:** > "Lass uns am @ocelot-social/ui Projekt weiterarbeiten" (packages/ui) **Nach jeder Session aktualisieren:** - "Fortschritt" – Balkendiagramme aktualisieren - "Aktueller Stand" – Zuletzt erledigte Aufgaben - "Meilensteine" – Checklisten abhaken - §12 "Arbeitsprotokoll" – Neue Einträge hinzufügen - `KATALOG.md` – Komponenten-Status pflegen --- ## Fortschritt ### Gesamtprojekt ``` Phase 0: ██████████ 100% (6/6 Aufgaben) ✅ Phase 1: ██████████ 100% (6/6 Aufgaben) ✅ Phase 2: ██████████ 100% (26/26 Aufgaben) ✅ Phase 3: █████████░ 83% (20/24 Aufgaben) - Webapp-Integration (M4a ✅, M5 ✅) Phase 4: █░░░░░░░░░ 6% (1/17 Aufgaben) - OsButton ✅ Phase 5: ░░░░░░░░░░ 0% (0/7 Aufgaben) ─────────────────────────────────────── Gesamt: ███████░░░ 69% (59/86 Aufgaben) ``` ### Katalogisierung (Details in KATALOG.md) ``` Webapp: ██████████ 100% (139 Komponenten erfasst) Styleguide: ██████████ 100% (38 Komponenten erfasst) Analyse: ██████████ 100% (Button, Modal, Menu detailiert) ``` ### OsButton Migration (Phase 3) ``` Scope gesamt: ~90 Buttons in Webapp ├─ Migriert: 32 Buttons (36%) ✅ ├─ Ohne neue Props: 0 Buttons (Milestone 4a ✅) └─ Mit icon/circle/loading: ~60 Buttons (Milestone 4c) OsButton Features: ├─ variant: ✅ primary, secondary, danger, warning, success, info, default ├─ appearance: ✅ filled, outline, ghost ├─ size: ✅ xs, sm, md, lg, xl ├─ disabled: ✅ mit hover/active-Override ├─ icon: ⬜ TODO (Milestone 4b) ├─ circle: ⬜ TODO (Milestone 4b) └─ loading: ⬜ TODO (Milestone 4b) ``` --- ## Aktueller Stand **Letzte Aktualisierung:** 2026-02-10 (Session 12) **Aktuelle Phase:** Phase 3 (Webapp-Integration) - Milestone 4a abgeschlossen ✅ (32 Buttons migriert, nächster: Milestone 4b) **Zuletzt abgeschlossen:** - [x] Projektordner erstellt - [x] Planungsdokument erstellt - [x] Tech-Stack entschieden - [x] Branding-Architektur definiert - [x] Migrationsstrategie definiert - [x] **Phase 0: Komponenten-Analyse** (177 Komponenten katalogisiert) - [x] Button-Familie detailiert analysiert (Props, Styles, Konsolidierung) - [x] Modal-Familie detailiert analysiert (Architektur erkannt) - [x] Menu-Familie detailiert analysiert (3 Patterns identifiziert) - [x] Priorisierung erstellt (15 Komponenten in 4 Tiers) - [x] Konsolidierungsplan finalisiert - [x] **Phase 1: Vue 2.7 Upgrade** ✅ - Vue 2.6.14 → 2.7.16 - vue-template-compiler entfernt - @vue/composition-api entfernt - @nuxtjs/composition-api entfernt - Webpack-Alias für @vue/composition-api → vue - Webpack-Regel für ESM .mjs Module - **Unit-Tests: 157 Suites, 979 passed, 87 Snapshots** ✅ - **Integrationstests: bestanden** ✅ - [x] **Phase 2: Projekt-Setup** ✅ - Vite + Vue 3 Projekt initialisiert - vue-demi für Vue 2/3 Kompatibilität - Vitest konfiguriert (integriert in vite.config.ts) - npm Package-Struktur mit korrekten exports - README.md Grundgerüst - LICENSE (Apache 2.0) - Plugin-Tests geschrieben - Tailwind CSS v4 mit @tailwindcss/vite - Dual-Build (style.css + tailwind.preset) - Dark Mode Grundstruktur (via Tailwind dark: Prefix) - Prop-Types definiert (Size, Rounded, Shadow, Variant) - Branding-Architektur (keine Defaults, validateCssVariables) - eslint-config-it4c eingerichtet (v0.8.0) - ESLint Flat Config mit Vue 3 + Vitest Modulen - Prettier-Integration via eslint-plugin-prettier - GitHub Workflows (ui-lint.yml, ui-test.yml, ui-build.yml) - 100% Test-Coverage Requirement - .tool-versions (Node 25.5.0, konsistent mit Dockerfiles) - Example Apps für Kompatibilitätstests (4er-Matrix) - GitHub Workflow ui-compatibility.yml für Vue 2/3 Tests (inkl. Lint) - Eigene ESLint + Prettier Configs für Example Apps - Type Assertions für CI-Kompatibilität (`as unknown as Plugin`) - Bundle Size Check (size-limit) mit ui-size.yml Workflow - Package-Validierung (publint, arethetypeswrong) mit CJS/ESM Types - Kompatibilitätstest-Workflow mit 4 Example Apps (Vue 2/3 × Tailwind/CSS) - release-please Manifest-Konfiguration (Monorepo-Setup) - npm Publish Workflow (ui-release.yml) - CONTRIBUTING.md (Entwickler-Leitfaden) - Dependabot für UI-Package und Example Apps konfiguriert - CSS-Build separat via Tailwind CLI (closeBundle Hook) - CVA (class-variance-authority) für typsichere Varianten - cn() Utility für Tailwind-Klassen-Merge (clsx + tailwind-merge) - OsButton Komponente mit CVA-Varianten implementiert - ESLint-Konfiguration angepasst (vue/max-attributes-per-line, import-x/no-relative-parent-imports) - Storybook 10 für Dokumentation eingerichtet (Wasserfarben-Theme) - OsButton.stories.ts mit Playground + allen Varianten/Appearances/Sizes - Storybook Build-Konfiguration (viteFinal entfernt Library-Plugins) - Docker Setup (Dockerfile, docker-compose, ui-docker.yml) - Visual Regression Tests (Playwright, colocated) mit integriertem A11y-Check - Completeness Check (verify Script prüft Story, Visual, checkA11y, Keyboard, Varianten) - ESLint Plugins: vuejs-accessibility, playwright, storybook, jsdoc **Aktuell in Arbeit:** - Phase 3, Milestone 4b: icon/circle/loading Props in OsButton implementieren - Phase 3, Milestone 4c: ~60 Buttons mit icon/circle/loading migrieren **Zuletzt abgeschlossen (Session 12 - CSS-Linting, CI-Optimierung, Code-Review Fixes):** - [x] CSS-Linting: `@eslint/css` + `tailwind-csstree` für Tailwind v4 Syntax-Support - [x] `excludeCSS()` Helper: JS-Regeln von CSS-Dateien fernhalten (language-Inkompatibilität) - [x] CSS-Regeln: `no-empty-blocks`, `no-duplicate-imports`, `no-invalid-at-rules` - [x] CI-Workflow-Trigger optimiert: 9 UI-Workflows von `on: push` auf Branch+Path-Filter (`master`, `packages/ui/**`) - [x] `custom-class` → `class` Migration: 4 Stellen in 3 Webapp-Dateien (notifications, MapStylesButtons, EmbedComponent) - [x] Vue 3 Template-Fix: `this.$t()` → `$t()` in CommentCard.vue (Zeile 5 + 58) - [x] Pre-existing Fix: `async` Arrow-Function in OsButton.visual.spec.ts **Zuvor abgeschlossen (Session 11 - Storybook & Code-Review Fixes):** - [x] Wasserfarben-Farbschema für Storybook (Ultramarin, Dioxazin-Violett, Alizarin, Ocker, Viridian, Cöruleum) - [x] Stories erweitert: Playground (interaktive Controls), alle Varianten in allen Stories - [x] Einzelne Stories (Primary, Secondary, Danger, Default) durch AllVariants ersetzt - [x] AllAppearances zeigt alle 7 Varianten × 3 Appearances - [x] Einheitlicher Border (0.8px) über alle Appearances (kein Layout-Shift mehr) - [x] WCAG 2.4.7 Fix: Default-Variante hat jetzt `focus:outline-dashed focus:outline-current` - [x] Keyboard Accessibility Test: prüft Focus-Indikator auf allen Buttons im Browser - [x] `data-appearance` Attribut: robuste CSS-Selektoren statt fragile escaped Tailwind-Klassen - [x] Code-Review Feedback eingearbeitet (Unit-Tests, Testnamen, CSS-Selektoren) **Zuvor abgeschlossen (Milestone 5 + Analyse):** - [x] Visuelle Validierung: 16/16 Buttons validiert ✅ - [x] OsButton Features: `appearance` (outline, ghost), `xs` size, focus/active states - [x] Disabled-Styles: CSS-Variablen, hover/active-Override, Border-Fix - [x] Codebase-Analyse: 14 weitere migrierbare Buttons identifiziert (Scope: 16/35) **Zuletzt erledigt (Phase 3):** - [x] vue-demi zur Webapp hinzugefügt (Vue 2.7 Kompatibilität) - [x] Webpack-Alias für vue-demi (nutzt Webapp's Vue 2.7 statt UI-Library's Vue 3) - [x] Webpack-Alias für @ocelot-social/ui (dist Pfade mit $ für exakten Match) - [x] OsButton mit isVue2 Render-Funktion (Vue 2: attrs-Objekt, Vue 3: flat props) - [x] CSS-Reihenfolge angepasst (UI-Library nach Styleguide für korrekte Spezifität) - [x] Manueller visueller Vergleich ✅ - [x] **Jest-Integration für vue-demi** ✅ - Custom Mock (`test/__mocks__/@ocelot-social/ui.js`) statt direktem Import - Problem: Jest's moduleNameMapper greift nicht für verschachtelte requires in CJS - Problem: Jest lädt `vue.runtime.common.js` mit exports unter `default` - Lösung: Module._load Patch für vue-demi + defineComponent von Vue.default - Setup-File (`test/vueDemiSetup.js`) für Module._resolveFilename Patch - **979 Tests bestehen ✅** - [x] Button-Variants an ds-button angepasst (font-semibold, rounded, box-shadow) - [x] UserTeaserPopover.vue migriert (verwendet ``) - [x] **Docker Build für UI-Library** ✅ - ui-library Stage in Dockerfile + Dockerfile.maintenance - COPY --from=ui-library ./app/ /packages/ui/ - [x] **CI-Kompatibilität** ✅ - Relativer Pfad `file:../packages/ui` statt absolut `/packages/ui` - Funktioniert lokal, in CI und in Docker - [x] **OsButton attrs/listeners Forwarding** ✅ - getCurrentInstance() für $listeners Zugriff in Vue 2 - inheritAttrs: false für manuelle Weiterleitung - Jest Mock um alle Composition API Funktionen erweitert - [x] **16 Buttons migriert** (ohne icon/circle/loading) ✅ - GroupForm.vue, EmbedComponent.vue, DonationInfo.vue, CommentCard.vue - MapStylesButtons.vue, GroupMember.vue, embeds.vue - notifications.vue, privacy.vue, terms-and-conditions-confirm.vue, UserTeaserPopover.vue - [x] **Disabled-Styles korrigiert** ✅ - CSS-Variablen `--color-disabled` und `--color-disabled-contrast` hinzugefügt - Filled-Buttons: Grauer Hintergrund statt opacity (wie buttonStates Mixin) - Outline/Ghost: Graue Border/Text - [x] terms-and-conditions-confirm.vue: Read T&C Button → `appearance="outline" variant="primary"` - [x] **Disabled:active/hover Spezifität** ✅ - CSS-Regeln in index.css mit höherer Spezifität für disabled:hover und disabled:active - Button zeigt sofort disabled-Farben, auch wenn während :active disabled wird - [x] notifications.vue: Check All + Uncheck All → `appearance="outline" variant="primary"` - [x] embeds.vue: Allow All → `appearance="outline" variant="primary"` - [x] **Disabled Border-Fix** ✅ - CSS-Regeln in index.css: `border-style: solid` und `border-width: 0.8px` bei disabled - Verhindert Layout-Sprung wenn Button disabled wird **Nächste Schritte:** 1. ~~Phase 0: Komponenten-Analyse~~ ✅ 2. ~~Phase 1: Vue 2.7 Upgrade~~ ✅ 3. ~~**Phase 2: Projekt-Setup**~~ ✅ ABGESCHLOSSEN 4. **Phase 3: Webapp-Integration** - 32/90 Buttons migriert (36%) - [x] yarn link / Webpack-Alias in Webapp - [x] CSS-Variablen definieren (ocelot-ui-variables.scss) - [x] 16 Buttons migriert & validiert ✅ - [x] Docker Build + CI-Kompatibilität - [x] **Milestone 4a:** 14 weitere Buttons (ohne neue Props) ✅ - [ ] **Milestone 4b:** icon/circle/loading Props implementieren - [ ] **Milestone 4c:** ~60 Buttons mit icon/circle/loading migrieren **Manuelle Setup-Aufgaben (außerhalb Code):** - [ ] `NPM_TOKEN` als GitHub Secret einrichten (für npm publish in ui-release.yml) - npm Token erstellen: https://www.npmjs.com/settings/ocelot-social/tokens - GitHub Secret: Repository → Settings → Secrets → Actions → New secret - [ ] Storybook auf externem Host deployen (via Webhook) - Server einrichten für Storybook-Hosting - Webhook-Endpoint erstellen (zieht + baut bei Release) - GitHub Webhook konfigurieren (trigger bei Release) --- ## Meilensteine ### Phase 0: Analyse & Katalogisierung ✅ - [x] Vollständige Katalogisierung Webapp-Komponenten (139 Komponenten) - [x] Vollständige Katalogisierung Styleguide-Komponenten (38 Komponenten) - [x] Duplikate identifizieren und dokumentieren (5 direkte + 3 Familien) - [x] Inkonsistenzen und Probleme erfassen (Button/Modal/Menu analysiert) - [x] Konsolidierungsplan erstellen (Token-Liste) - [x] Priorisierung der zu migrierenden Komponenten (15 Komponenten in 4 Tiers) ### Phase 1: Vue 2.7 Upgrade ✅ - [x] Vue 2.6 → Vue 2.7 Upgrade in Webapp (2.6.14 → 2.7.16) - [x] Abhängigkeiten aktualisieren: - [x] vue-template-compiler entfernt (in Vue 2.7 eingebaut) - [x] @vue/composition-api entfernt (in Vue 2.7 eingebaut) - [x] @nuxtjs/composition-api entfernt (nicht mehr nötig) - [x] vue-server-renderer auf 2.7.16 aktualisiert - [x] Tests durchführen: **157 Suites, 979 passed, 87 Snapshots** ✅ - [x] Regressionstests (`yarn dev` und manuelle Prüfung) ✅ ### Phase 2: Projekt-Setup ✅ - [x] Vite + Vue 3 Projekt initialisieren - [x] vue-demi einrichten für Vue 2 Kompatibilität - [x] Tailwind CSS einrichten (v4 mit @tailwindcss/vite) - [x] Dual-Build konfigurieren (Tailwind Preset + vorkompilierte CSS) - [x] CSS Custom Properties Token-System aufsetzen (requiredCssVariables + validateCssVariables) - [x] Dark Mode Grundstruktur (via Tailwind `dark:` Prefix, dokumentiert) - [x] Storybook für Dokumentation einrichten - [x] Vitest konfigurieren - [x] eslint-config-it4c einrichten (v0.8.0: TypeScript, Vue 3, Vitest, Prettier) - [x] npm Package-Struktur (@ocelot-social/ui) mit korrekten exports - [x] Vue 2/3 Kompatibilitätstests (via Example Apps) - [x] GitHub Workflows einrichten (ui-lint.yml, ui-test.yml, ui-build.yml, ui-compatibility.yml) - [x] Docker Setup (Dockerfile, docker-compose, ui-docker.yml Workflow) - [x] Visual Regression Tests einrichten (Playwright, colocated mit Komponenten) - [x] Accessibility Tests in Visual Tests integriert (@axe-core/playwright) - [x] Keyboard Accessibility Tests (describe('keyboard accessibility')) - [x] ESLint Plugins: vuejs-accessibility, playwright, storybook, jsdoc, @eslint/css - [x] Bundle Size Check einrichten (size-limit, ui-size.yml) - [x] Package-Validierung einrichten (publint, arethetypeswrong) - [x] Example Apps erstellen (vue3-tailwind, vue3-css, vue2-tailwind, vue2-css) - [x] Kompatibilitätstest-Workflow einrichten (4er-Matrix, siehe §18) - [x] release-please Manifest-Konfiguration - [x] npm Publish Workflow (ui-release.yml) - [x] Storybook Build Workflow (ui-storybook.yml) - [x] LICENSE Datei (Apache 2.0) - [x] README.md Grundgerüst (Installation, Quick Start, Struktur) - [x] CONTRIBUTING.md - [x] Completeness Check Script (Story, Visual+checkA11y, Keyboard, Varianten) ### Phase 3: Webapp-Integration (Validierung) **Ziel:** OsButton in der Webapp einbinden, ohne visuelle oder funktionale Änderungen. **Ansatz:** Integration First - Library einbinden, dann schrittweise OsButton ersetzen, beginnend mit einfachsten Stellen. **Milestone 1: Library-Einbindung** ✅ - [x] @ocelot-social/ui in Webapp installieren (yarn link + Webpack-Alias) - [x] vue-demi zur Webapp hinzugefügt (für Vue 2.7 Kompatibilität) - [x] Webpack-Alias für vue-demi (nutzt Webapp's Vue 2.7) - [x] Webpack-Alias für @ocelot-social/ui$ und style.css$ - [x] CSS Custom Properties in Webapp definieren (ocelot-ui-variables.scss) - [x] CSS-Reihenfolge angepasst (UI-Library nach Styleguide) - [x] Import-Pfade testen - [x] Docker Build Stage für UI-Library (Dockerfile + Dockerfile.maintenance) - [x] Relativer Pfad für CI-Kompatibilität (file:../packages/ui) - [x] Jest Mock für @ocelot-social/ui (test/__mocks__/@ocelot-social/ui.js) **Milestone 2: Erste Integration (Minimaler Aufwand)** ✅ - [x] OsButton mit isVue2 Render-Funktion (Vue 2/3 kompatibel) - [x] Button-Variants an ds-button angepasst (font-semibold, rounded, box-shadow) - [x] OsButton in UserTeaserPopover.vue eingesetzt (`variant="primary"`) - [x] Manueller visueller Vergleich ✅ - [x] Webapp-Tests bestehen ✅ (979 Tests, jest moduleNameMapper für vue-demi) **Milestone 3: Schrittweise Erweiterung** ✅ - [x] GroupForm.vue Cancel-Button migriert - [x] OsButton attrs/listeners Forwarding (Vue 2 $listeners via getCurrentInstance) - [x] 14 weitere Buttons migriert (alle ohne icon/circle/loading) **Milestone 4a: Weitere Buttons migrieren (14 ohne neue Props)** - [ ] Modal Cancel-Buttons (DisableModal, DeleteUserModal, ReleaseModal) - [ ] Form Cancel/Submit-Buttons (ContributionForm, EnterNonce, MySomethingList) - [ ] ImageUploader.vue (2× Crop-Buttons) - [ ] Page-Buttons (donations, badges, notifications/index, profile Unblock/Unmute) - [ ] ReportRow.vue More-Details-Button **Milestone 4b: OsButton Props erweitern** - [ ] `icon` Prop implementieren (slot-basiert oder Icon-Komponente) - [ ] `circle` Variant zu CVA hinzufügen - [ ] `loading` Prop mit Spinner implementieren **Milestone 4c: Buttons mit icon/circle/loading migrieren (~60 Buttons)** *Button-Komponenten (Wrapper):* - [ ] Button/JoinLeaveButton.vue (icon, loading) - [ ] Button/FollowButton.vue (icon, loading) - [ ] LoginButton/LoginButton.vue (icon, circle) - [ ] InviteButton/InviteButton.vue (icon, circle) - [ ] EmotionButton/EmotionButton.vue (circle) - [ ] CustomButton/CustomButton.vue (2× circle) - [ ] LabeledButton/LabeledButton.vue (icon, circle) *Navigation & Menus:* - [ ] ContentMenu/ContentMenu.vue (icon, circle) - [ ] ContentMenu/GroupContentMenu.vue (icon, circle) - [ ] ChatNotificationMenu.vue (circle) - [ ] NotificationMenu.vue (3× icon, circle) - [ ] HeaderMenu/HeaderMenu.vue (icon, circle) - [ ] Map/MapButton.vue (circle) *Editor:* - [ ] Editor/MenuBarButton.vue (icon, circle) - [ ] Editor/MenuLegend.vue (~10× icon, circle) *Filter & Input:* - [ ] HashtagsFilter.vue (icon, circle) - [ ] CategoriesSelect.vue (icon) - [ ] SearchableInput.vue (icon, circle) - [ ] Select/LocationSelect.vue (icon) - [ ] PaginationButtons.vue (2× icon, circle) *Chat:* - [ ] Chat/Chat.vue (2× icon, circle) - [ ] Chat/AddChatRoomByUserSearch.vue (icon, circle) *Forms & Auth:* - [ ] LoginForm/LoginForm.vue (icon, loading) - [ ] PasswordReset/Request.vue (loading) - [ ] PasswordReset/ChangePassword.vue (loading) - [ ] Password/Change.vue (loading) - [ ] ContributionForm.vue Submit (icon, loading) - [ ] GroupForm.vue Submit (icon) - [ ] CommentForm/CommentForm.vue (loading) *Modals:* - [ ] Modal/ConfirmModal.vue (2× icon, loading) - [ ] Modal/ReportModal.vue (2× icon, loading) - [ ] Modal/DisableModal.vue Confirm (icon) - [ ] Modal/DeleteUserModal.vue Confirm (icon) - [ ] Modal/ReleaseModal.vue Confirm (icon) *Features:* - [ ] ComponentSlider.vue (2× icon, loading) - [ ] MySomethingList.vue (3× icon, circle, loading) - [ ] CreateInvitation.vue (icon, circle) - [ ] Invitation.vue (2× icon, circle) - [ ] ProfileList.vue (loading) - [ ] ReportRow.vue Confirm (icon) - [ ] ImageUploader.vue Delete/Cancel (2× icon, circle) - [ ] CommentCard.vue Reply (icon, circle) - [ ] EmbedComponent.vue Close (icon, circle) - [ ] CtaUnblockAuthor.vue (icon) - [ ] data-download.vue (icon, loading) *Pages:* - [ ] pages/groups/_id/_slug.vue (3× icon, circle, loading) - [ ] pages/admin/users/index.vue (2× icon, circle, loading) - [ ] pages/settings/index.vue (icon, loading) - [ ] pages/settings/blocked-users.vue (icon, circle) - [ ] pages/settings/muted-users.vue (icon, circle) - [ ] pages/settings/my-email-address/*.vue (2× icon) - [ ] pages/profile/_id/_slug.vue Chat (icon) - [ ] pages/post/_id/_slug/index.vue (icon, circle) **Milestone 5: Validierung & Dokumentation** ✅ - [x] Keine visuellen Änderungen bestätigt (16/16 Buttons validiert) - [x] Keine funktionalen Änderungen bestätigt - [x] Disabled-Styles korrigiert (hover/active-Override, Border-Fix) - [ ] Webapp-Tests bestehen weiterhin (TODO: Regressionstest) - [ ] Erkenntnisse in KATALOG.md dokumentiert **Einsatzstellen-Übersicht:** | Kategorie | Buttons | Status | |-----------|---------|--------| | ✅ Migriert & Validiert | 24 | Erledigt | | ⏳ Ohne neue Props (M4a) | 6 | In Arbeit (8 von 14 erledigt) | | ⬜ Mit icon/circle/loading (M4c) | ~60 | Ausstehend | | **Gesamt** | **~90** | **27% erledigt** | **Details siehe KATALOG.md** (vollständige Tracking-Tabellen) **Erfolgskriterien:** | Kriterium | Prüfung | |-----------|---------| | Visuell identisch | Manueller Screenshot-Vergleich | | Funktional identisch | Click, Disabled funktionieren | | Keine Regression | Webapp Unit-Tests bestehen | **Visuelle Validierung (OsButton vs Original):** Jeder migrierte Button muss manuell geprüft werden: Normal, Hover, Focus, Active, Disabled. | Datei | Button | Props | Validiert | |-------|--------|-------|-----------| | `components/Group/GroupForm.vue` | Cancel | `default` | ✅ | | `components/Group/GroupMember.vue` | Remove Member | `appearance="outline" variant="primary" size="sm"` | ✅ | | `components/CommentCard/CommentCard.vue` | Show more/less | `appearance="ghost" variant="primary" size="sm"` | ✅ | | `components/UserTeaser/UserTeaserPopover.vue` | Open Profile | `variant="primary"` | ✅ | | `components/DonationInfo/DonationInfo.vue` | Donate Now | `size="sm" variant="primary"` | ✅ | | `components/Map/MapStylesButtons.vue` | Map Styles | `:appearance` dynamisch + custom CSS | ✅ | | `components/Embed/EmbedComponent.vue` | Cancel | `appearance="outline" variant="danger"` + custom CSS | ✅ | | `components/Embed/EmbedComponent.vue` | Play Now | `variant="primary"` + custom CSS | ✅ | | `pages/terms-and-conditions-confirm.vue` | Read T&C | `appearance="outline" variant="primary"` | ✅ | | `pages/terms-and-conditions-confirm.vue` | Save | `variant="primary"` + disabled | ✅ | | `pages/settings/privacy.vue` | Save | `variant="primary"` + disabled | ✅ | | `pages/settings/notifications.vue` | Check All | `appearance="outline" variant="primary"` + disabled | ✅ | | `pages/settings/notifications.vue` | Uncheck All | `appearance="outline" variant="primary"` + disabled | ✅ | | `pages/settings/notifications.vue` | Save | `variant="primary"` + disabled | ✅ | | `pages/settings/embeds.vue` | Allow All | `appearance="outline" variant="primary"` + disabled | ✅ | | `pages/settings/embeds.vue` | Deny All | `variant="primary"` + disabled | ✅ | **Validierung abgeschlossen:** 16/16 (100%) ✅ **Nach Abschluss aller Validierungen:** - [ ] Gesamt-Regressionstest durchführen - [ ] Alle Unit-Tests bestehen - [ ] Dokumentation aktualisieren ### Phase 4: Komponenten-Migration (15 Komponenten + 2 Infrastruktur) **Tier 1: Kern-Komponenten** - [ ] OsIcon (vereint DsIcon + BaseIcon) - [ ] OsSpinner (vereint DsSpinner + LoadingSpinner) - [x] OsButton (vereint DsButton + BaseButton) ✅ Entwickelt in Phase 2 - [ ] OsCard (vereint DsCard + BaseCard) **Tier 2: Layout & Feedback** - [ ] OsModal (Basis: DsModal) - [ ] OsDropdown (Basis: Webapp Dropdown) - [ ] OsAvatar (vereint DsAvatar + ProfileAvatar) - [ ] OsInput (Basis: DsInput) **Tier 3: Navigation & Typography** - [ ] OsMenu (Basis: DsMenu) - [ ] OsMenuItem (Basis: DsMenuItem) - [ ] OsHeading (Basis: DsHeading) - [ ] OsText (Basis: DsText) **Tier 4: Spezial-Komponenten** - [ ] OsSelect - [ ] OsTable - [ ] OsTag **Infrastruktur** - [ ] System-Icons einrichten - [ ] CI docs-check Workflow (JSDoc-Coverage, README-Aktualität) ### Phase 5: Finalisierung - [ ] Alle Komponenten migriert und getestet - [ ] Alte Komponenten aus Vue 2 Projekt entfernt - [ ] Build als npm Library verifiziert - [ ] README.md finalisieren (alle Sektionen vollständig) - [ ] ARCHITECTURE.md erstellen (aus PROJEKT.md §2, §4, §5, §11, §15, §16) - [ ] PROJEKT.md und KATALOG.md archivieren (docs/archive/) - [ ] Dokumentation vollständig und CI-geprüft --- --- # VISION ## 1. Projektziel & Vision **Kurzbeschreibung:** Neue Vue 3 Komponentenbibliothek aufbauen, die später die Vue 2 Komponenten in der Webapp ersetzen soll. **Hintergrund:** - Bestehendes Projekt nutzt Vue 2.7 mit Nuxt 2 (Upgrade von 2.6 → 2.7 in Phase 1 erledigt ✅) - Existierender `styleguide` Ordner als Git-Submodul (Vue 2, Vue CLI 3) - Design-Token-System mit Theo vorhanden - Branding erfolgt über SCSS-Dateien mit Variablen-Overrides - **Problem:** Viele doppelte Komponenten, inkonsistente Styles, nicht konsequent genutztes Design-System **Vision:** Ein stark definiertes und flexibles Design-System, das den Branding-Anforderungen von ocelot.social gerecht wird und eine saubere, schrittweise Migration von Vue 2 nach Vue 3 ermöglicht. **Geplanter Ansatz:** Migration vorbereiten - schrittweise neue Komponenten in Vue 3 entwickeln, die das bestehende Design-System respektieren und flexible Branding-Optionen bieten. --- # TECHNISCHE GRUNDLAGEN ## 2. Tech-Stack | Komponente | Entscheidung | Notizen | |------------|--------------|---------| | Framework | **Vue 3 + Vite** | Schnellstes Setup, modernes Tooling | | Build-Tool | **Vite** | Schnelles HMR, einfache Konfiguration | | Dokumentation | **Storybook 10** | Komponenten-Dokumentation mit Vue 3 + Vite | | Styling | **Tailwind CSS** | Mit CSS Custom Properties für Branding | | Testing | **Vitest** | Vite-nativ, Jest-kompatible API | | Paket-Name | **@ocelot-social/ui** | Unter ocelot-social npm Org | | Komponenten-Prefix | **Os** | OsButton, OsCard, etc. | | Vue 2 Kompatibilität | **vue-demi** | Library funktioniert in Vue 2 und Vue 3 | | Varianten-System | **CVA** | class-variance-authority für typsichere Prop-Varianten | | Klassen-Merge | **cn()** | clsx + tailwind-merge für Klassen-Kombination | | Linting | **eslint-config-it4c** | Enthält: TypeScript, Vue, Prettier, weitere Regeln | | Release | **release-please** | Automatische Versionen und Changelogs | | Icons | **Hybrid-Architektur** | System-Icons in Library, Feature-Icons in App (siehe §4) | | Browser-Support | **Modern only** | Chrome, Firefox, Safari, Edge (letzte 2 Versionen) | | SSR | **Ja** | Nuxt-kompatibel | | Dark Mode | **Ja, von Anfang an** | Alle Komponenten mit Light/Dark Varianten | | Lizenz | **Apache 2.0** | Permissiv mit Patent-Schutz | | Repository | **Monorepo** | Ocelot-Social/packages/ui/ | ### Konventionen | Aspekt | Entscheidung | Notizen | |--------|--------------|---------| | Vue API | **` ``` ### Checkliste bei neuer Komponente Vor dem Erstellen einer Komponente diese Fragen beantworten: ``` [ ] Wo gehört die Komponente hin? (Entscheidungsbaum durchlaufen) [ ] Falls Library: Sind alle Texte via Props? [ ] Falls Library: Keine direkten Store/Router Imports? [ ] Falls Webapp: Welche Library-Komponenten werden genutzt? [ ] Falls Grenzfall: Kann sie aufgeteilt werden? ``` --- ## 16a. Webapp ↔ Maintenance Code-Sharing ### Problemstellung Die Webapp und Maintenance-App sind aktuell verschachtelt und sollen getrennt werden. Einige Business-Komponenten werden in beiden Apps benötigt, gehören aber nicht in die UI-Library. **Das DX-Problem:** "shared" hat kein logisches Kriterium außer "wird in beiden gebraucht". ### Analysierte Optionen | Option | Beschreibung | Bewertung | |--------|--------------|-----------| | **A: Domain Packages** | `@ocelot-social/auth`, `@ocelot-social/posts`, etc. | Gut bei vielen Komponenten, aber Overhead | | **B: Core + Duplikation** | Composables teilen, Komponenten duplizieren | Gut wenn UI unterschiedlich | | **C: Webapp als Source** | Maintenance importiert aus Webapp | Einfachste Lösung | ### Empfehlung: Option C (Webapp als Source of Truth) ``` ┌─────────────────────────────────────────────────────────────┐ │ @ocelot-social/ui │ │ ───────────────── │ │ • OsButton, OsModal, OsCard, OsInput │ │ • Rein präsentational, keine Abhängigkeiten │ ├─────────────────────────────────────────────────────────────┤ │ webapp/ │ │ ─────── │ │ • Alle Business-Komponenten (Source of Truth) │ │ • Composables in webapp/lib/composables/ │ │ • GraphQL in webapp/graphql/ │ │ • Ist die "Haupt-App" │ ├─────────────────────────────────────────────────────────────┤ │ maintenance/ │ │ ──────────── │ │ • Importiert aus @ocelot-social/ui │ │ • Importiert aus webapp/ via Alias │ │ • Nur maintenance-spezifische Komponenten lokal │ └─────────────────────────────────────────────────────────────┘ ``` ### Umsetzung **maintenance/nuxt.config.js:** ```javascript export default { alias: { '@webapp': '../webapp', '@ocelot-social/ui': '../packages/ui/dist' } } ``` **Import in Maintenance:** ```typescript // UI-Komponenten aus Library import { OsButton, OsModal } from '@ocelot-social/ui' // Business-Komponenten aus Webapp import FollowButton from '@webapp/components/FollowButton.vue' import PostTeaser from '@webapp/components/PostTeaser.vue' // Composables aus Webapp import { useAuth } from '@webapp/lib/composables/useAuth' import { useFollow } from '@webapp/lib/composables/useFollow' ``` ### Kriterien für Entwickler | Frage | Antwort | |-------|---------| | Wo suche ich eine UI-Komponente? | `@ocelot-social/ui` | | Wo suche ich eine Business-Komponente? | `webapp/components/` | | Wo erstelle ich eine neue geteilte Komponente? | `webapp/components/` | | Wo erstelle ich maintenance-spezifische Komponenten? | `maintenance/components/` | ### Vorteile 1. **Klare Regel:** Alles Business-bezogene ist in Webapp 2. **Kein neues Package:** Weniger Overhead 3. **Eine Source of Truth:** Keine Sync-Probleme 4. **Einfache Migration:** Später ggf. Domain-Packages extrahieren ### Spätere Evolution (optional) Wenn klare Patterns entstehen, können Domain-Packages extrahiert werden: ``` Phase 1 (jetzt): Webapp ist Source of Truth Phase 2 (später): Patterns identifizieren Phase 3 (später): @ocelot-social/auth, @ocelot-social/posts, etc. ``` ### Entscheidung | # | Datum | Entscheidung | |---|-------|--------------| | 68 | 2026-02-09 | Webapp als Source of Truth für geteilte Business-Komponenten | --- ## 16b. Daten-Entkopplung (ViewModel/Mapper Pattern) ### Problemstellung Komponenten sind oft direkt an API/GraphQL-Strukturen gekoppelt: ```vue // Komponente kennt GraphQL-Struktur props.user.avatar.url props.user._followedByCurrentUserCount // Underscore?! props.user.__typename // Leaked! ``` **Probleme:** - Schema-Änderung = alle Komponenten anpassen - `__typename`, `_count` etc. leaken in die UI - Schwer testbar (braucht echte GraphQL-Struktur) - Komponenten nicht wiederverwendbar ### Lösung: ViewModel + Mapper Pattern ``` ┌─────────────────────────────────────────────────────────────┐ │ GraphQL / API Layer │ │ • Queries & Mutations │ │ • Generated Types (graphql-codegen) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Mappers (einziger Ort der API-Struktur kennt) │ │ • toUserCardViewModel(graphqlUser) → UserCardViewModel │ │ • toPostTeaserViewModel(graphqlPost) → PostTeaserViewModel │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ ViewModels (was die UI braucht) │ │ • UserCardViewModel { displayName, avatarUrl, ... } │ │ • PostTeaserViewModel { title, excerpt, ... } │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Presentational Components (kennen nur ViewModels) │ │ • │ │ • │ └─────────────────────────────────────────────────────────────┘ ``` ### Implementierung **1. ViewModels definieren:** ```typescript // types/viewModels.ts export interface UserCardViewModel { id: string displayName: string avatarUrl: string | null followerCount: number isFollowedByMe: boolean } export interface PostTeaserViewModel { id: string title: string excerpt: string authorName: string authorAvatarUrl: string | null createdAt: Date commentCount: number canEdit: boolean } ``` **2. Mapper-Funktionen:** ```typescript // mappers/userMapper.ts import type { UserCardViewModel } from '~/types/viewModels' import type { UserGraphQL } from '~/graphql/types' export function toUserCardViewModel( user: UserGraphQL, currentUserId?: string ): UserCardViewModel { return { id: user.id, displayName: user.name || user.slug || 'Anonymous', avatarUrl: user.avatar?.url ?? null, followerCount: user._followedByCurrentUserCount ?? 0, isFollowedByMe: user.followedByCurrentUser ?? false, } } ``` **3. Komponenten nutzen nur ViewModels:** ```vue ``` **4. Composables kapseln Mapping:** ```typescript // composables/useUser.ts import { computed } from 'vue' import { useQuery } from '@vue/apollo-composable' import { GET_USER } from '~/graphql/queries' import { toUserCardViewModel } from '~/mappers/userMapper' import type { UserCardViewModel } from '~/types/viewModels' export function useUser(userId: string) { const { result, loading, error } = useQuery(GET_USER, { id: userId }) const user = computed(() => { if (!result.value?.User) return null return toUserCardViewModel(result.value.User) }) return { user, loading, error } } ``` ### Ordnerstruktur ``` webapp/ ├── graphql/ │ ├── queries/ │ ├── mutations/ │ └── types/ # Generated by graphql-codegen ├── types/ │ └── viewModels.ts # UI-spezifische Interfaces ├── mappers/ │ ├── userMapper.ts │ ├── postMapper.ts │ └── index.ts ├── lib/ │ └── composables/ │ ├── useAuth.ts │ ├── useUser.ts # Gibt ViewModel zurück │ └── usePost.ts ├── components/ # Presentational (nur ViewModels) │ ├── UserCard.vue │ └── PostTeaser.vue └── pages/ # Nutzen Composables └── users/[id].vue ``` ### Vorteile | Aspekt | Ohne Mapper | Mit Mapper | |--------|-------------|------------| | **API-Änderung** | Alle Komponenten anpassen | Nur Mapper anpassen | | **Testing** | Mock GraphQL Response | Einfaches ViewModel-Objekt | | **Wiederverwendung** | Komponente an API gebunden | Komponente API-agnostisch | | **TypeScript** | Komplexe/generierte Types | Klare, einfache Interfaces | | **webapp ↔ maintenance** | Verschiedene Strukturen | Gleiche ViewModels | ### Regeln ``` ┌─────────────────────────────────────────────────────────────┐ │ Regel 1: Komponenten definieren was sie BRAUCHEN │ │ (ViewModel), nicht was die API LIEFERT │ ├─────────────────────────────────────────────────────────────┤ │ Regel 2: Mapper sind der EINZIGE Ort der API kennt │ │ API-Änderung = nur Mapper ändern │ ├─────────────────────────────────────────────────────────────┤ │ Regel 3: Composables kapseln Fetching + Mapping │ │ useUser() gibt UserCardViewModel zurück │ ├─────────────────────────────────────────────────────────────┤ │ Regel 4: Presentational Components sind API-agnostisch │ │ Einfach testbar, wiederverwendbar │ └─────────────────────────────────────────────────────────────┘ ``` ### Entscheidung | # | Datum | Entscheidung | |---|-------|--------------| | 70 | 2026-02-09 | ViewModel/Mapper Pattern für Daten-Entkopplung | --- ## 17. Externe Abhängigkeiten ### Übersicht | Abhängigkeit | Status | Beschreibung | |--------------|--------|--------------| | **eslint-config-it4c** | ⚠️ ESLint 10 ausstehend | v0.8.0 eingebunden, ESLint 10 inkompatibel | ### eslint-config-it4c **Status:** Funktional, ESLint 10 Update ausstehend **TODO:** eslint-config-it4c muss für ESLint 10 aktualisiert werden (aktuell inkompatibel wegen @typescript-eslint/utils). **Lösung:** Das Paket wurde in Version 0.8.0 modularisiert und unterstützt jetzt ESLint Flat Config. **Aktuelle Nutzung in @ocelot-social/ui:** ```typescript // eslint.config.ts import config, { vue3, vitest } from 'eslint-config-it4c' export default [ ...config, // Base + TypeScript + Prettier + weitere ...vue3, // Vue 3 Regeln ...vitest, // Vitest Test-Regeln // Projekt-spezifische Overrides... ] ``` **Projekt-spezifische Anpassungen:** - `n/file-extension-in-import`: Ausnahmen für `.vue`, `.css`, `.scss`, `.json` - `import-x/no-unassigned-import`: CSS-Imports erlaubt - `vitest/consistent-test-filename`: Pattern `*.spec.ts` - `vitest/prefer-expect-assertions`: Ausgeschaltet - `vitest/no-hooks`: Ausgeschaltet - Example Apps: Eigene ESLint-Configs (ignoriert in Hauptpaket, gelintet im Compatibility-Workflow) --- ## 18. Kompatibilitätstests (Details) ### Testmatrix Die Library muss in 4 Kombinationen funktionieren: ``` │ Tailwind │ CSS (vorkompiliert) ────────────────────┼───────────────────┼────────────────────── Vue 3.4+ │ ✓ Muss testen │ ✓ Muss testen Vue 2.7 │ ✓ Muss testen │ ✓ Muss testen ``` ### Werkzeuge & Strategien #### 1. vue-demi (Code-Ebene) Abstrahiert Vue 2/3 API-Unterschiede: ```typescript // Komponente importiert von vue-demi, nicht vue import { ref, computed, defineComponent } from 'vue-demi' ``` #### 2. Unit Tests mit Vitest Hauptpaket testet nur mit Vue 3 (Entwicklungsumgebung). Vue 2 Kompatibilität wird via vue-demi gewährleistet und in Example Apps getestet. **Begründung:** Inline Vue 2/3 Matrix verursacht Peer-Dependency-Konflikte. #### 3. Example Apps (4 Kombinationen) - Hauptstrategie für Vue 2 Tests ``` packages/ui/ ├── examples/ │ ├── vue3-tailwind/ # Vite + Vue 3 + Tailwind Preset │ │ ├── package.json │ │ ├── vite.config.ts │ │ ├── tailwind.config.js # Nutzt @ocelot-social/ui/tailwind.preset │ │ └── src/App.vue # Importiert alle Komponenten │ │ │ ├── vue3-css/ # Vite + Vue 3 + style.css │ │ ├── package.json │ │ ├── vite.config.ts │ │ └── src/ │ │ ├── main.ts # import '@ocelot-social/ui/style.css' │ │ └── App.vue │ │ │ ├── vue2-tailwind/ # Vue CLI / Nuxt 2 + Tailwind │ │ └── ... │ │ │ └── vue2-css/ # Vue CLI / Nuxt 2 + style.css │ └── ... ``` **Jede Example App:** - Importiert `@ocelot-social/ui` via Workspace-Link - Rendert alle exportierten Komponenten - Kann lokal gestartet werden (`npm run dev`) - Wird in CI gebaut und getestet #### 4. Playwright E2E Tests ```typescript // e2e/compatibility.spec.ts import { test, expect } from '@playwright/test' const examples = [ { name: 'vue3-tailwind', port: 3001 }, { name: 'vue3-css', port: 3002 }, { name: 'vue2-tailwind', port: 3003 }, { name: 'vue2-css', port: 3004 }, ] for (const example of examples) { test.describe(example.name, () => { test('all components render', async ({ page }) => { await page.goto(`http://localhost:${example.port}`) // Prüfe dass alle Komponenten sichtbar sind await expect(page.locator('[data-testid="os-button"]')).toBeVisible() await expect(page.locator('[data-testid="os-card"]')).toBeVisible() await expect(page.locator('[data-testid="os-modal"]')).toBeVisible() }) test('styles are applied correctly', async ({ page }) => { await page.goto(`http://localhost:${example.port}`) const button = page.locator('[data-testid="os-button-primary"]') // Prüfe dass CSS korrekt angewendet wird await expect(button).toHaveCSS('background-color', /rgb/) }) test('visual regression', async ({ page }) => { await page.goto(`http://localhost:${example.port}`) await expect(page).toHaveScreenshot(`${example.name}.png`) }) }) } ``` #### 5. Package Validation | Tool | Zweck | |------|-------| | **publint** | Prüft package.json auf Export-Fehler | | **arethetypeswrong** | Prüft TypeScript-Typen für alle Entry Points | ```json { "scripts": { "check:exports": "publint && attw --pack .", "prepublishOnly": "npm run check:exports" } } ``` #### 6. package.json Exports (korrekte Struktur) ```json { "name": "@ocelot-social/ui", "type": "module", "exports": { ".": { "import": "./dist/index.mjs", "require": "./dist/index.cjs", "types": "./dist/index.d.ts" }, "./style.css": "./dist/style.css", "./tailwind.preset": { "import": "./dist/tailwind.preset.mjs", "require": "./dist/tailwind.preset.cjs", "types": "./dist/tailwind.preset.d.ts" } }, "peerDependencies": { "vue": "^2.7.0 || ^3.0.0" }, "peerDependenciesMeta": { "tailwindcss": { "optional": true } } } ``` ### CI Workflow: Compatibility Matrix ```yaml # .github/workflows/compatibility.yml name: Compatibility Tests on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' - run: npm ci - run: npm run build - uses: actions/upload-artifact@v4 with: name: dist path: packages/ui/dist test-unit: needs: build runs-on: ubuntu-latest strategy: fail-fast: false matrix: vue-version: ['2.7', '3.4'] steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: dist path: packages/ui/dist - run: npm ci - run: npm test env: VUE_VERSION: ${{ matrix.vue-version }} test-e2e: needs: build runs-on: ubuntu-latest strategy: fail-fast: false matrix: example: ['vue3-tailwind', 'vue3-css', 'vue2-tailwind', 'vue2-css'] steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: dist path: packages/ui/dist - name: Setup example app working-directory: packages/ui/examples/${{ matrix.example }} run: | npm ci npm run build - name: Start preview server working-directory: packages/ui/examples/${{ matrix.example }} run: npm run preview & - name: Wait for server run: npx wait-on http://localhost:4173 --timeout 30000 - name: Run Playwright tests working-directory: packages/ui run: npx playwright test --grep "${{ matrix.example }}" - uses: actions/upload-artifact@v4 if: failure() with: name: playwright-report-${{ matrix.example }} path: packages/ui/playwright-report/ validate-package: needs: build runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: dist path: packages/ui/dist - run: npm ci - name: Validate exports run: | npx publint packages/ui npx @arethetypeswrong/cli packages/ui ``` ### Werkzeug-Übersicht | Werkzeug | Zweck | Phase | |----------|-------|-------| | **vue-demi** | Vue 2/3 API-Kompatibilität im Code | Phase 2 | | **Vitest + Matrix** | Unit Tests für Vue 2.7 und Vue 3.4 | Phase 2 | | **Example Apps (4x)** | Echte Projekte für jede Kombination | Phase 2 | | **Playwright** | E2E + Visual Regression für alle 4 | Phase 2 | | **publint** | Package.json Export-Validierung | Phase 2 | | **arethetypeswrong** | TypeScript Entry Points Check | Phase 2 | | **pkg-pr-new** | Preview-Releases in PRs (optional) | Optional | ### Checkliste für neue Komponenten ``` [ ] Unit Tests laufen mit VUE_VERSION=2.7 [ ] Unit Tests laufen mit VUE_VERSION=3.4 [ ] Komponente in allen 4 Example Apps hinzugefügt [ ] E2E Tests für Komponente geschrieben [ ] Visual Regression Baseline erstellt [ ] Keine Vue 3-only APIs verwendet (oder via vue-demi abstrahiert) ``` --- ## 19. Komplexitätsanalyse ### Umfang nach Phasen | Phase | Aufgaben | Komplexität | Abhängigkeiten | |-------|----------|-------------|----------------| | Phase 0: Analyse | 6 Tasks | ✅ Erledigt | - | | Phase 1: Vue 2.7 | 6 Tasks | ✅ Erledigt | - | | Phase 2: Setup | 26 Tasks | Mittel | ⚠️ eslint-config-it4c (extern) | | Phase 3: Tokens | 6 Tasks | Niedrig | Keine externen | | Phase 4: Migration | 15 Komponenten | Hoch | Pro Komponente: Spec→Dev→Test→Integrate | | Phase 5: Finalisierung | 7 Tasks | Niedrig | Alle vorherigen Phasen | ### Bekannte Risikofaktoren | Risiko | Beschreibung | Auswirkung | Mitigation | |--------|--------------|------------|------------| | **eslint-config-it4c** | Externes Projekt muss zuerst modularisiert werden | Blockiert Linting-Setup in Phase 2 | Temporäre lokale ESLint-Config als Workaround | | **vue-demi Kompatibilität** | Unbekannte Edge-Cases bei Vue 2/3 Dual-Support | Unerwartete Bugs bei Integration | Frühzeitig in Example Apps testen | | **Visual Regression Baselines** | Können bei Design-Änderungen viel Nacharbeit erfordern | Zusätzlicher Aufwand bei Änderungen | Baselines erst nach Design-Freeze erstellen | | **Feature-Parity** | Alte Komponenten haben undokumentierte Verhaltensweisen | Regressions bei Migration | Gründliche Analyse vor Implementierung | | **Tailwind + CSS Dual-Build** | Komplexe Build-Konfiguration | Build-Fehler, Inkonsistenzen | Früh beide Varianten parallel testen | ### Parallelisierbarkeit | Phase | Parallelisierbar | Details | |-------|------------------|---------| | Phase 2 | Teilweise | Die meisten Tasks sind sequentiell (Setup-Reihenfolge wichtig) | | Phase 3 | Nein | Token-Ebenen bauen aufeinander auf (Base → Semantic → Component) | | Phase 4 | Ja (nach Tier 1) | Tier 2/3 Komponenten können parallel entwickelt werden | | Phase 5 | Teilweise | Dokumentation kann parallel zur letzten Integration | **Parallelisierbare Aufgaben in Phase 2:** - 4 Example Apps (vue3-tailwind, vue3-css, vue2-tailwind, vue2-css) - GitHub Workflows (unabhängig voneinander) - LICENSE, README.md, CONTRIBUTING.md **Sequentielle Abhängigkeiten in Phase 2:** 1. Vite + Vue 3 Projekt → vue-demi → Tailwind → Build-Pipeline 2. Vitest → Tests → Visual Regression 3. Package-Struktur → release-please → npm Publish Workflow ### Aufwandstreiber pro Komponente (Phase 4) Jede Komponente durchläuft: ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ ANALYSE │ → │ SPEC │ → │ DEVELOP │ → │ QA │ → │ INTEGRATE │ ├─────────────┤ ├─────────────┤ ├─────────────┤ ├─────────────┤ ├─────────────┤ │ Bestehende │ │ Props │ │ Vue 3 Code │ │ Unit Tests │ │ In Webapp │ │ Varianten │ │ Events │ │ TypeScript │ │ Vue 2 Tests │ │ einbinden │ │ analysieren │ │ Slots │ │ Tailwind │ │ A11y Tests │ │ │ │ │ │ Tokens │ │ Storybook │ │ Visual Reg. │ │ Alte Komp. │ │ │ │ A11y │ │ Stories │ │ 4 Examples │ │ entfernen │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ ``` **Aufwand variiert stark nach Komponente:** | Komponente | Komplexität | Grund | |------------|-------------|-------| | OsIcon | Niedrig | Einfache Wrapper-Komponente | | OsSpinner | Niedrig | Nur Animation + Größen | | OsButton | Hoch | Viele Varianten, Link-Support, States | | OsCard | Niedrig | Einfaches Layout | | OsModal | Hoch | Teleport, Focus-Trap, Animations, A11y | | OsDropdown | Hoch | Positioning, Click-Outside, Hover-States | | OsInput | Mittel | Validierung, States, Icons | | OsAvatar | Niedrig | Bild + Fallback |