From 951a24f100cf735751ea43750186fa36fada9171 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 20 Feb 2026 05:49:02 +0100 Subject: [PATCH] feat(package/ui): os-badge (#9250) --- packages/ui/KATALOG.md | 75 +++++---- packages/ui/PROJEKT.md | 90 ++++++---- .../ui/src/components/OsBadge/OsBadge.spec.ts | 157 ++++++++++++++++++ .../src/components/OsBadge/OsBadge.stories.ts | 140 ++++++++++++++++ .../components/OsBadge/OsBadge.visual.spec.ts | 97 +++++++++++ .../ui/src/components/OsBadge/OsBadge.vue | 96 +++++++++++ .../__screenshots__/chromium/all-shapes.png | Bin 0 -> 1927 bytes .../__screenshots__/chromium/all-sizes.png | Bin 0 -> 5046 bytes .../__screenshots__/chromium/all-variants.png | Bin 0 -> 3866 bytes .../__screenshots__/chromium/form-counter.png | Bin 0 -> 11364 bytes .../__screenshots__/chromium/with-icon.png | Bin 0 -> 2672 bytes .../src/components/OsBadge/badge.variants.ts | 46 +++++ packages/ui/src/components/OsBadge/index.ts | 8 + packages/ui/src/components/index.ts | 8 + packages/ui/src/tailwind.preset.spec.ts | 6 +- packages/ui/src/tailwind.preset.ts | 60 ++++--- webapp/components/Category/index.vue | 14 +- .../ContributionForm/ContributionForm.vue | 56 +++++-- webapp/components/Group/GroupForm.vue | 60 +++++-- webapp/components/Group/GroupMember.vue | 7 +- webapp/components/Group/GroupTeaser.vue | 17 +- webapp/components/Hashtag/Hashtag.vue | 7 +- webapp/components/PostTeaser/PostTeaser.vue | 3 +- .../_id/__snapshots__/_slug.spec.js.snap | 154 +++++++---------- webapp/pages/groups/_id/_slug.vue | 17 +- 25 files changed, 865 insertions(+), 253 deletions(-) create mode 100644 packages/ui/src/components/OsBadge/OsBadge.spec.ts create mode 100644 packages/ui/src/components/OsBadge/OsBadge.stories.ts create mode 100644 packages/ui/src/components/OsBadge/OsBadge.visual.spec.ts create mode 100644 packages/ui/src/components/OsBadge/OsBadge.vue create mode 100644 packages/ui/src/components/OsBadge/__screenshots__/chromium/all-shapes.png create mode 100644 packages/ui/src/components/OsBadge/__screenshots__/chromium/all-sizes.png create mode 100644 packages/ui/src/components/OsBadge/__screenshots__/chromium/all-variants.png create mode 100644 packages/ui/src/components/OsBadge/__screenshots__/chromium/form-counter.png create mode 100644 packages/ui/src/components/OsBadge/__screenshots__/chromium/with-icon.png create mode 100644 packages/ui/src/components/OsBadge/badge.variants.ts create mode 100644 packages/ui/src/components/OsBadge/index.ts diff --git a/packages/ui/KATALOG.md b/packages/ui/KATALOG.md index f6b783d0d..832edfd85 100644 --- a/packages/ui/KATALOG.md +++ b/packages/ui/KATALOG.md @@ -13,7 +13,7 @@ Phase 0: Analyse ██████████ 100% (8/8 Schritte) ✅ Phase 3: OsButton ██████████ 100% (133/133 Buttons) ✅ Phase 4: Tier 1 ██████████ 100% (OsButton, OsIcon, OsSpinner, OsCard) ✅ Phase 4: Tier A → HTML ██████████ 100% (10 ds-* Wrapper → Plain HTML) ✅ -Phase 4: Tier B → HTML ░░░░░░░░░░ 0% (ds-chip, ds-number, ds-grid, ds-radio) +Phase 4: Tier B ██████░░░░ 60% (ds-chip→OsBadge✅, ds-tag→OsBadge✅, ds-grid✅, ds-number⬜, ds-radio⬜) Phase 4: Tier 2-4 ░░░░░░░░░░ 0% (OsModal, OsInput, OsMenu, OsSelect, OsTable) ``` @@ -23,9 +23,10 @@ Phase 4: Tier 2-4 ░░░░░░░░░░ 0% (OsModal, OsInput, Os | Webapp Komponenten | 139 | | Styleguide Komponenten | 38 (23 in Webapp genutzt) | | **Gesamt** | **177** | -| ✅ UI-Library | OsButton, OsIcon, OsSpinner, OsCard (4) | -| ✅ → Plain HTML | Section, Placeholder, Tag, List, ListItem, Container, Heading, Text, Space, Flex, FlexItem (11) | -| ⬜ → Plain HTML | Chip, Number, Grid, GridItem, Radio (5) — Tier B | +| ✅ UI-Library | OsButton, OsIcon, OsSpinner, OsCard, OsBadge (5) | +| ✅ → Plain HTML | Section, Placeholder, List, ListItem, Container, Heading, Text, Space, Flex, FlexItem, Grid, GridItem (12) | +| ✅ → OsBadge | Chip (20 Nutzungen → OsBadge), Tag (3 → OsBadge shape="square") | +| ⬜ → Plain HTML | Number, Radio (2) — Tier B Rest | | ⬜ → UI-Library | Modal, Input, Menu, MenuItem, Select, Table (6) — Tier 2-4 | | ⬜ Offen | Form (18 Dateien — HTML oder OsForm?) | | ⬜ Nicht in Webapp | Code, CopyField, FormItem, InputError, InputLabel, Page, PageTitle, Logo, Avatar, TableCol, TableHeadCol (11) | @@ -53,7 +54,7 @@ Phase 4: Tier 2-4 ░░░░░░░░░░ 0% (OsModal, OsInput, Os |---|------------|--------|---------| | 1 | Avatar | ⬜ Nicht genutzt | Webapp nutzt eigenes ProfileAvatar | | 2 | Card | ✅ UI-Library | → OsCard (BaseCard gelöscht) | -| 3 | Chip | ⬜ Tier B | 5 Dateien → Plain HTML `` | +| 3 | Chip | ✅ UI-Library | → OsBadge (20 Nutzungen in 5 Dateien) | | 4 | Code | ⬜ Nicht genutzt | Nicht in Webapp verwendet | | 5 | Icon | ✅ UI-Library | → OsIcon (BaseIcon gelöscht, 82 Ocelot-Icons) | | 6 | Number | ⬜ Tier B | 5 Dateien → Plain HTML `
` | @@ -62,7 +63,7 @@ Phase 4: Tier 2-4 ░░░░░░░░░░ 0% (OsModal, OsInput, Os | 9 | Table | ⬜ Tier 4 | 7 Dateien → OsTable | | 10 | TableCol | ⬜ Tier 4 | Intern von Table genutzt | | 11 | TableHeadCol | ⬜ Tier 4 | Intern von Table genutzt | -| 12 | Tag | ✅ → HTML | Tier A: `` | +| 12 | Tag | ✅ UI-Library | → OsBadge shape="square" (3 Nutzungen in 3 Dateien) | ### Data Input | # | Komponente | Status | Notizen | @@ -83,8 +84,8 @@ Phase 4: Tier 2-4 ░░░░░░░░░░ 0% (OsModal, OsInput, Os | 22 | Container | ✅ → HTML | Tier A: `
` | | 23 | Flex | ✅ → HTML | Tier A: Plain HTML + CSS @media Queries | | 24 | FlexItem | ✅ → HTML | Tier A: Plain HTML + CSS @media Queries | -| 25 | Grid | ⬜ Tier B | 2 Dateien → CSS Grid | -| 26 | GridItem | ⬜ Tier B | 8 Dateien → CSS Grid | +| 25 | Grid | ✅ → HTML | 2 Dateien → CSS Grid (class="ds-grid") | +| 26 | GridItem | ✅ → HTML | 8 Dateien → CSS Grid | | 27 | Modal | ⬜ Tier 2 | 7 Dateien → OsModal | | 28 | Page | ⬜ Nicht genutzt | Nicht direkt in Webapp verwendet | | 29 | PageTitle | ⬜ Nicht genutzt | Nicht direkt in Webapp verwendet | @@ -202,7 +203,7 @@ Phase 4: Tier 2-4 ░░░░░░░░░░ 0% (OsModal, OsInput, Os ### H-L | # | Komponente | Status | Kategorie | Styleguide-Pendant | Notizen | |---|------------|--------|-----------|-------------------|---------| -| 64 | Hashtag | ⬜ Ausstehend | Display | Tag/Chip | | +| 64 | Hashtag | ✅ Migriert | Display | Tag/Chip | 🔄 nutzt OsBadge shape="square" | | 65 | HashtagsFilter | ⬜ Ausstehend | Filter | | | | 66 | HeaderButton | ✅ Migriert | Button | Button | 🔄 Button-Familie, nutzt OsButton | | 67 | HeaderMenu | ⬜ Ausstehend | Navigation | Menu | | @@ -370,10 +371,12 @@ Phase 4: Tier 2-4 ░░░░░░░░░░ 0% (OsModal, OsInput, Os ### Layout & Typography — → Plain HTML ✅ (Tier A) - ~~Container, Flex, FlexItem, Section, Space~~ ✅ → HTML + CSS -- ~~Heading, Text, List, ListItem, Tag, Placeholder~~ ✅ → HTML + CSS +- ~~Heading, Text, List, ListItem, Placeholder~~ ✅ → HTML + CSS +- ~~Chip, Tag~~ ✅ → OsBadge (UI-Library) -### Noch zu migrieren (Tier B → Plain HTML) -- Chip, Number, Grid, GridItem, Radio +### Noch zu migrieren (Tier B Rest) +- Number (5 Dateien), Radio (1 Datei) +- ~~Grid, GridItem~~ ✅ → CSS Grid (Plain HTML) ### Feature-Komponenten (bleiben in Webapp) - Chat, Group, Registration, Search, etc. @@ -430,11 +433,12 @@ Phase 4: Tier 2-4 ░░░░░░░░░░ 0% (OsModal, OsInput, Os 12. [x] _ds-compat.scss Utility-Klassen 13. [x] 10 ds-* Wrapper → HTML + CSS (~450 Nutzungen, ~90 Dateien) -### Phase 4: Tier B — ds-* → Plain HTML (ausstehend) -14. [ ] ds-chip (5 Dateien) → `` -15. [ ] ds-number (5 Dateien) → `
` -16. [ ] ds-grid / ds-grid-item (10 Dateien) → CSS Grid -17. [ ] ds-radio (1 Datei) → native `` +### Phase 4: Tier B — ds-* Migration (60%) +14. [x] ds-chip (5 Dateien, 20 Nutzungen) → OsBadge (UI-Library) +15. [x] ds-tag (3 Dateien) → OsBadge shape="square" (UI-Library) +16. [x] ds-grid / ds-grid-item (10 Dateien) → CSS Grid (Plain HTML) +17. [ ] ds-number (5 Dateien) → `
` +18. [ ] ds-radio (1 Datei) → native `` ### Phase 4: Tier 2-4 — UI-Library (ausstehend) 18. [ ] OsModal (7 Dateien) @@ -445,7 +449,7 @@ Phase 4: Tier 2-4 ░░░░░░░░░░ 0% (OsModal, OsInput, Os --- -**✅ Phase 0-3 abgeschlossen. Phase 4: Tier 1 + Tier A ✅, Tier B + Tier 2-4 ausstehend.** +**✅ Phase 0-3 abgeschlossen. Phase 4: Tier 1 + Tier A ✅, Tier B 60% (Chip→OsBadge, Tag→OsBadge, Grid→HTML), Tier 2-4 ausstehend.** --- @@ -945,19 +949,19 @@ interface OsDropdownProps { | # | Komponente | Status | |---|------------|--------| -| — | ds-section, ds-placeholder, ds-tag, ds-list, ds-list-item | ✅ → HTML-Elemente + CSS-Klassen | +| — | ds-section, ds-placeholder, ds-list, ds-list-item | ✅ → HTML-Elemente + CSS-Klassen | | — | ds-container, ds-heading, ds-text | ✅ → HTML-Elemente + CSS-Klassen | | — | ds-space | ✅ → div + Margin-Utility-Klassen | | — | ds-flex, ds-flex-item | ✅ → HTML + CSS @media Queries | -### Tier B: Einfache ds-* → Plain HTML (ausstehend) +### Tier B: Einfache ds-* Migration (60%) -| # | Komponente | Dateien | Ziel | -|---|------------|---------|------| -| — | ds-chip | 5 | `` | -| — | ds-number | 5 | `
` | -| — | ds-grid / ds-grid-item | 10 | CSS Grid | -| — | ds-radio | 1 | native `` | +| # | Komponente | Dateien | Ziel | Status | +|---|------------|---------|------|--------| +| 5 | **OsBadge** | — | ds-chip (20 Nutzungen, 5 Dateien) + ds-tag (3 Dateien) | ✅ | +| — | ds-grid / ds-grid-item | 10 | CSS Grid (Plain HTML) | ✅ | +| — | ds-number | 5 | `
` | ⬜ | +| — | ds-radio | 1 | native `` | ⬜ | ### Tier 2: Layout & Feedback (ausstehend) @@ -998,21 +1002,26 @@ interface OsDropdownProps { 2. OsSpinner ✅ Vereint: DsSpinner + LoadingSpinner 3. OsButton ✅ Vereint: DsButton + BaseButton → 133 Buttons in 79 Dateien 4. OsCard ✅ Vereint: DsCard + BaseCard → ~30 Dateien +5. OsBadge ✅ Vereint: ds-chip (20 Nutzungen) + ds-tag (3 Dateien) ``` ### Tier A: Triviale ds-* Wrapper → Plain HTML ✅ ``` -ds-section, ds-placeholder, ds-tag, ds-list, ds-list-item ✅ → HTML + CSS-Klassen -ds-container, ds-heading, ds-text ✅ → HTML + CSS-Klassen -ds-space ✅ → div + Margin-Utilities -ds-flex, ds-flex-item ✅ → HTML + CSS @media Queries +ds-section, ds-placeholder, ds-list, ds-list-item ✅ → HTML + CSS-Klassen +ds-container, ds-heading, ds-text ✅ → HTML + CSS-Klassen +ds-space ✅ → div + Margin-Utilities +ds-flex, ds-flex-item ✅ → HTML + CSS @media Queries ``` -### Tier B: Einfache ds-* → Plain HTML (ausstehend) +### Tier B: Einfache ds-* Migration (60%) ``` -ds-chip, ds-number, ds-grid/ds-grid-item, ds-radio → Plain HTML + CSS +ds-chip → OsBadge (UI-Library) ✅ +ds-tag → OsBadge shape="square" (UI-Library) ✅ +ds-grid / ds-grid-item → CSS Grid (HTML) ✅ +ds-number → Plain HTML ⬜ (5 Dateien) +ds-radio → native ⬜ (1 Datei) ``` ### Tier 2-4: UI-Library (ausstehend) @@ -1162,4 +1171,4 @@ $box-shadow-small-inset: inset 0 0 0 1px rgba(0,0,0,.05) **OsButton Migration: ✅ Vollständig abgeschlossen.** - BaseButton.vue gelöscht, base-components.js Plugin entfernt - Alle Tests, Snapshots, Cypress E2E-Selektoren aktualisiert -- Nächster Schritt: Tier B (ds-chip, ds-number, ds-grid, ds-radio) oder Tier 2 (OsModal) +- Nächster Schritt: Tier B Rest (ds-number, ds-radio) oder Tier 2 (OsModal) diff --git a/packages/ui/PROJEKT.md b/packages/ui/PROJEKT.md index 13351fce0..35ffc4827 100644 --- a/packages/ui/PROJEKT.md +++ b/packages/ui/PROJEKT.md @@ -81,10 +81,10 @@ Phase 0: ██████████ 100% (6/6 Aufgaben) ✅ Phase 1: ██████████ 100% (6/6 Aufgaben) ✅ Phase 2: ██████████ 100% (26/26 Aufgaben) ✅ Phase 3: ██████████ 100% (24/24 Aufgaben) ✅ - Webapp-Integration komplett -Phase 4: █████░░░░░ 46% (12/26 Aufgaben) - Tier 1 ✅, Tier A ✅, Infra ✅ | Tier B, Tier 2-4 ausstehend +Phase 4: ██████░░░░ 54% (14/26 Aufgaben) - Tier 1 ✅, Tier A ✅, Infra ✅, OsBadge ✅ | Tier B (rest), Tier 2-4 ausstehend Phase 5: ░░░░░░░░░░ 0% (0/7 Aufgaben) ─────────────────────────────────────── -Gesamt: ████████░░ 78% (74/95 Aufgaben) +Gesamt: ████████░░ 80% (76/95 Aufgaben) ``` ### Katalogisierung (Details in KATALOG.md) @@ -186,38 +186,50 @@ Tier A ds-* → Plain HTML + CSS: ✅ ├─ ds-flex/ds-flex-item: JavaScript window.innerWidth → CSS @media Queries │ (kein Layout-Shift bei SSR, bessere Performance) ├─ system.css bleibt geladen — bestehende CSS-Klassen funktionieren weiter -├─ Verbleibend: 13 ds-* Komponenten (Tier B: 4 einfache, Tier C: 6 komplexe → UI-Library) +├─ Verbleibend: 9 ds-* Komponenten (Tier B Rest: 2 einfache, Tier C: 6 komplexe → UI-Library) └─ 0 Tier-A ds-* Komponenten-Tags verbleibend + +ds-chip + ds-tag → OsBadge (UI-Library): ✅ +├─ OsBadge Komponente: CVA-Varianten, h() Render-Function, inheritAttrs: false +├─ Props: variant (default/primary/danger), size (sm/md/lg), shape (pill/square) +├─ Types: BadgeVariant, BadgeSize, BadgeShape (+ BadgeVariants) +├─ ds-chip: 20 Nutzungen in 5 Dateien → (Formzähler + Gruppen-Metadaten) +├─ ds-tag: 3 Nutzungen in 3 Dateien → (Category, Hashtag) +├─ ARIA: role="status" aria-live="polite" auf Form-Zähler (11 Stellen in 2 Dateien) +├─ CSS-Variable: --color-default + --color-default-contrast (beide in requiredCssVariables) +├─ 18 Unit-Tests, 6 Stories, 5 Visual Tests + 1 Keyboard Test +└─ 0 ds-chip/ds-tag Nutzungen verbleibend ``` --- ## Aktueller Stand -**Letzte Aktualisierung:** 2026-02-19 (Session 28) +**Letzte Aktualisierung:** 2026-02-20 (Session 30) -**Aktuelle Phase:** Phase 4 - OsIcon ✅, BaseIcon → OsIcon Migration ✅, OsSpinner ✅, Spinner Webapp-Migration ✅, OsCard ✅, BaseCard → OsCard Migration ✅, Tier A ds-* → Plain HTML ✅ +**Aktuelle Phase:** Phase 4 - Tier 1 ✅, Tier A ✅, OsBadge ✅ (ds-chip + ds-tag → UI-Library) | Tier B, Tier 2-4 ausstehend -**Zuletzt abgeschlossen (Session 28 - CodeRabbit Review Fixes für Tier A PR):** -- [x] GroupForm.vue: Fehlende `buttons`-Klasse auf Button-Container ergänzt (CSS-Regel `.group-form > .buttons` griff nicht) -- [x] ComponentSlider.vue: `

` → `

` (Original war ``, falsch zu h1 migriert) -- [x] SearchHeading.vue: `

` → `

` (Original war ``, falsch zu h1 migriert) -- [x] MySomethingList.vue: Redundantes verschachteltes `
` entfernt (Überbleibsel von verschachtelten ds-space) -- [x] Empty.vue: Prop-Type `[String, Object]` → `String` (Object-Support war ds-space-Relikt, nicht implementiert) -- [x] ChangePassword.vue: Inline-Spans → separate `

` Elemente (visuelle Regression: fehlende Block-Level-Trennung) -- [x] RegistrationSlideEmail.vue: Label `for="checkbox0"` → `for="sendEmailAgain"` passend zu `id="sendEmailAgain"` (A11y WCAG 1.3.1/4.1.2) -- [x] Signup.vue: `margin="large"` (totes ds-space-Attribut auf div) → `class="ds-my-large"` + `