From fc714a5f303cb443b52799090db0cdffa7a1e7bb Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 15 Feb 2026 21:31:00 +0100 Subject: [PATCH] feat(package/ui): os-icon (#9234) --- packages/ui/PROJEKT.md | 410 ++++++------------ packages/ui/eslint.config.ts | 7 + packages/ui/package.json | 17 +- packages/ui/playwright.config.ts | 2 +- packages/ui/scripts/check-completeness.ts | 123 ++++-- .../components/OsButton/OsButton.stories.ts | 174 ++++---- .../ui/src/components/OsIcon/OsIcon.spec.ts | 225 ++++++++++ .../src/components/OsIcon/OsIcon.stories.ts | 143 ++++++ .../components/OsIcon/OsIcon.visual.spec.ts | 100 +++++ packages/ui/src/components/OsIcon/OsIcon.vue | 119 +++++ .../__screenshots__/chromium/all-sizes.png | Bin 0 -> 3063 bytes .../chromium/all-system-icons.png | Bin 0 -> 4636 bytes .../chromium/custom-component.png | Bin 0 -> 1471 bytes .../__screenshots__/chromium/in-button.png | Bin 0 -> 6266 bytes .../chromium/inherit-color.png | Bin 0 -> 2226 bytes .../ui/src/components/OsIcon/icon.variants.ts | 10 + .../ui/src/components/OsIcon/icons/index.ts | 17 + .../components/OsIcon/icons/svgs/check.svg | 3 + .../components/OsIcon/icons/svgs/close.svg | 3 + .../src/components/OsIcon/icons/svgs/plus.svg | 3 + packages/ui/src/components/OsIcon/index.ts | 3 + packages/ui/src/components/index.ts | 9 + .../src/ocelot/icons/OcelotIcons.stories.ts | 46 ++ .../ocelot/icons/OcelotIcons.visual.spec.ts | 73 ++++ .../__screenshots__/chromium/all-icons.png | Bin 0 -> 9187 bytes packages/ui/src/ocelot/icons/index.spec.ts | 62 +++ packages/ui/src/ocelot/icons/index.ts | 24 + .../ui/src/ocelot/icons/svgs/angle-down.svg | 5 + packages/ui/src/ocelot/index.ts | 2 + packages/ui/src/plugins/vite-svg-icon.ts | 64 +++ packages/ui/src/styles/index.css | 1 + packages/ui/src/svg-icon.d.ts | 6 + packages/ui/vite.config.ts | 8 +- webapp/components/AvatarMenu/AvatarMenu.vue | 8 +- .../Chat/AddChatRoomByUserSearch.vue | 5 +- webapp/components/Chat/Chat.vue | 6 +- .../ContributionForm/ContributionForm.vue | 5 +- .../DropdownFilter/DropdownFilter.vue | 8 +- webapp/components/Embed/EmbedComponent.vue | 5 +- .../FilterMenu/CategoriesFilter.vue | 5 +- .../components/FilterMenu/EventsByFilter.vue | 5 +- webapp/components/FilterMenu/FilterMenu.vue | 9 +- .../components/FilterMenu/FollowingFilter.vue | 5 +- webapp/components/FilterMenu/HeaderButton.vue | 6 +- .../HashtagsFilter/HashtagsFilter.vue | 6 +- .../components/LocaleSwitch/LocaleSwitch.vue | 8 +- webapp/components/Modal/ReportModal.vue | 5 +- webapp/components/Select/LocationSelect.vue | 6 +- webapp/components/Uploader/ImageUploader.vue | 5 +- .../features/Invitations/CreateInvitation.vue | 6 +- .../CreateInvitation.spec.js.snap | 26 +- .../__snapshots__/InvitationList.spec.js.snap | 26 +- .../SearchableInput/SearchableInput.vue | 5 +- webapp/jest.config.js | 1 + webapp/nuxt.config.js | 1 + .../_id/__snapshots__/_slug.spec.js.snap | 78 +++- webapp/pages/groups/_id/_slug.vue | 5 +- .../_id/__snapshots__/invites.spec.js.snap | 13 +- webapp/pages/groups/index.vue | 5 +- webapp/pages/index.vue | 5 +- .../_id/__snapshots__/_slug.spec.js.snap | 26 +- webapp/pages/profile/_id/_slug.vue | 5 +- webapp/pages/settings/index.vue | 5 +- .../settings/my-email-address/enter-nonce.vue | 6 +- .../pages/settings/my-email-address/index.vue | 5 +- .../__mocks__/@ocelot-social/ui/ocelot.js | 82 ++++ 66 files changed, 1560 insertions(+), 496 deletions(-) create mode 100644 packages/ui/src/components/OsIcon/OsIcon.spec.ts create mode 100644 packages/ui/src/components/OsIcon/OsIcon.stories.ts create mode 100644 packages/ui/src/components/OsIcon/OsIcon.visual.spec.ts create mode 100644 packages/ui/src/components/OsIcon/OsIcon.vue create mode 100644 packages/ui/src/components/OsIcon/__screenshots__/chromium/all-sizes.png create mode 100644 packages/ui/src/components/OsIcon/__screenshots__/chromium/all-system-icons.png create mode 100644 packages/ui/src/components/OsIcon/__screenshots__/chromium/custom-component.png create mode 100644 packages/ui/src/components/OsIcon/__screenshots__/chromium/in-button.png create mode 100644 packages/ui/src/components/OsIcon/__screenshots__/chromium/inherit-color.png create mode 100644 packages/ui/src/components/OsIcon/icon.variants.ts create mode 100644 packages/ui/src/components/OsIcon/icons/index.ts create mode 100644 packages/ui/src/components/OsIcon/icons/svgs/check.svg create mode 100644 packages/ui/src/components/OsIcon/icons/svgs/close.svg create mode 100644 packages/ui/src/components/OsIcon/icons/svgs/plus.svg create mode 100644 packages/ui/src/components/OsIcon/index.ts create mode 100644 packages/ui/src/ocelot/icons/OcelotIcons.stories.ts create mode 100644 packages/ui/src/ocelot/icons/OcelotIcons.visual.spec.ts create mode 100644 packages/ui/src/ocelot/icons/__screenshots__/chromium/all-icons.png create mode 100644 packages/ui/src/ocelot/icons/index.spec.ts create mode 100644 packages/ui/src/ocelot/icons/index.ts create mode 100755 packages/ui/src/ocelot/icons/svgs/angle-down.svg create mode 100644 packages/ui/src/ocelot/index.ts create mode 100644 packages/ui/src/plugins/vite-svg-icon.ts create mode 100644 packages/ui/src/svg-icon.d.ts create mode 100644 webapp/test/__mocks__/@ocelot-social/ui/ocelot.js diff --git a/packages/ui/PROJEKT.md b/packages/ui/PROJEKT.md index 96fedf07e..f3a992e48 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: █░░░░░░░░░ 6% (1/17 Aufgaben) - OsButton ✅ +Phase 4: ██░░░░░░░░ 18% (3/17 Aufgaben) - OsButton ✅, OsIcon ✅, System-Icons ✅ Phase 5: ░░░░░░░░░░ 0% (0/7 Aufgaben) ─────────────────────────────────────── -Gesamt: ████████░░ 74% (63/86 Aufgaben) +Gesamt: ████████░░ 76% (65/86 Aufgaben) ``` ### Katalogisierung (Details in KATALOG.md) @@ -115,21 +115,49 @@ OsButton Features: as-Prop Migration: 15 /-Wrapper in 15 Webapp-Dateien → as="nuxt-link"/as="a" ``` +### OsIcon (Phase 4) +``` +OsIcon Features: +├─ name: ✅ System-Icon per Name (check, close, plus) +├─ icon: ✅ Custom Vue-Komponente (hat Vorrang vor name) +├─ size: ✅ xs, sm, md, lg, xl, 2xl (em-basiert) +├─ a11y: ✅ decorative (default) / semantic (mit aria-label) +├─ color: ✅ fill-current (erbt von Parent) +└─ svg-plugin: ✅ vite-svg-icon (SVG → Vue Component via ?icon) + +System-Icons: +├─ check.svg (Checkmark) +├─ close.svg (Close/X) +└─ plus.svg (Plus/Add) + +Ocelot-Icons (separates Entry-Point): +└─ angle-down.svg (Dropdown-Pfeil) +``` + --- ## Aktueller Stand -**Letzte Aktualisierung:** 2026-02-14 (Session 20) +**Letzte Aktualisierung:** 2026-02-15 (Session 21) -**Aktuelle Phase:** Phase 3 ✅ ABGESCHLOSSEN + Code-Review-Feedback + `as`-Prop-Migration +**Aktuelle Phase:** Phase 4 - OsIcon ✅ implementiert, System-Icons eingerichtet -**Abgeschlossene Phasen:** -- [x] Phase 0: Analyse (177 Komponenten katalogisiert) -- [x] Phase 1: Vue 2.7 Upgrade (2.6.14 → 2.7.16, 979 Tests ✅) -- [x] Phase 2: Projekt-Setup (Vite, vue-demi, Tailwind v4, CVA, Storybook 10, CI/CD, 100% Coverage) -- [x] Phase 3: Webapp-Integration — 133 os-button in 79 Dateien, 0 base-button/ds-button verbleibend +**Zuletzt abgeschlossen (Session 21 - OsIcon Komponente, System-Icons, Ocelot-Umbenennung):** +- [x] OsIcon Komponente implementiert (name, icon, size Props; Vue 2/3 via vue-demi h()) +- [x] System-Icons: check, close, plus (SVG, viewBox 0 0 32 32, stroke-basiert) +- [x] Custom vite-svg-icon Plugin: SVG → Vue Render-Function via `?icon` Query +- [x] Icon-Größen: xs(0.75em), sm(0.875em), md(1.2em), lg(1.5em), xl(2em), 2xl(2.5em) +- [x] A11y: decorative (aria-hidden, default) / semantic (role="img" + aria-label) +- [x] fill-current für Farbvererbung vom Parent +- [x] OsButton nutzt OsIcon statt inline SVG für Icon-Rendering +- [x] Ocelot-Icons: separates Entry-Point (ocelot.mjs) mit dynamischem Loading via import.meta.glob +- [x] `src/webapp/` → `src/ocelot/` umbenannt (konsistentes Naming) +- [x] check-completeness erweitert: unterstützt ocelot/ Verzeichnis +- [x] OsIcon: 211 Zeilen Unit-Tests, Visual Tests mit checkA11y(), Keyboard A11y +- [x] 100% Test-Coverage für OsIcon +- [x] OsButton Stories bereinigt (OsIcon statt Inline-SVGs) -**Zuletzt abgeschlossen (Session 20 - `as`-Prop + nuxt-link Migration):** +**Zuvor abgeschlossen (Session 20 - `as`-Prop + nuxt-link Migration):** - [x] OsButton: `as` Prop implementiert (polymorphe Komponente: `button`, `a`, `nuxt-link`, `router-link`, Custom-Komponenten) - [x] Naming-Konvention: `tag` → `as` (moderner Standard: Headless UI, Radix Vue, Chakra UI, PrimeVue) - [x] `disabled`/`type`/`loading` nur bei `as="button"` (Links haben kein natives `disabled`-Attribut) @@ -146,246 +174,34 @@ as-Prop Migration: 15 /-Wrapper in 15 Webapp-Dateien → as="nuxt - NotificationMenu.vue (3 Instanzen, 2 zu einem Button konsolidiert via counter-icon) - [x] Verifiziert: 0 verbleibende ``/``-Wrapper um `` in Webapp -**Zuvor abgeschlossen (Session 19 - CodeRabbit Review Feedback: Cleanup, Accessibility, Bugfixes):** -- [x] donations.vue: Redundantes `:checked="showDonations"` entfernt (v-model setzt checked bereits) -- [x] MySomethingList.vue: Disabled-Logik vereinfacht `!(!isEditing || (isEditing && !disabled))` → `isEditing && disabled` -- [x] button.variants.ts: Hardcoded Fallback `#e5e3e8` entfernt → `var(--color-disabled)` (konsistent mit filled/index.css) -- [x] CommentCard.vue: `aria-label` auf icon-only Reply-Button -- [x] HashtagsFilter.vue: `aria-label` auf icon-only Clear-Button -- [x] ReleaseModal.vue: `$emit('close')` im catch-Block ergänzt (fehlte im Fehlerfall) -- [x] Chat.vue: `aria-label` auf Expand- und Close-Buttons -- [x] i18n: `chat.expandChat` + `chat.closeChat` in allen 9 Sprachdateien (vollständig übersetzt) -- [x] ChatNotificationMenu.vue: `aria-label` auf icon-only Chat-Button -- [x] SearchableInput.vue: `aria-label` auf icon-only Close-Button -- [x] GroupButton.vue: `aria-label` auf icon-only Groups-Button -- [x] MapButton.vue: `aria-label` auf icon-only Map-Button -- [x] EmotionButton.vue: `aria-label` auf icon-only Emoji-Button (`