From 9b98dcae9ae870f628ec2c30c62b77879549f6db Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 10 Feb 2026 21:56:32 +0100 Subject: [PATCH] refactor(webapp): vue3 migration - phase 3 - integration (#9180) --- .github/file-filters.yml | 5 + .github/workflows/ui-build.yml | 20 + .github/workflows/ui-compatibility.yml | 23 +- .github/workflows/ui-docker.yml | 20 + .github/workflows/ui-lint.yml | 20 + .github/workflows/ui-size.yml | 20 + .github/workflows/ui-storybook.yml | 20 + .github/workflows/ui-test.yml | 20 + .github/workflows/ui-verify.yml | 20 + .github/workflows/ui-visual.yml | 20 + packages/ui/.storybook/preview.ts | 2 + packages/ui/.storybook/storybook.css | 56 +- packages/ui/KATALOG.md | 255 ++++++- packages/ui/PROJEKT.md | 660 +++++++++++++++++- packages/ui/eslint.config.ts | 34 +- packages/ui/package-lock.json | 123 ++-- packages/ui/package.json | 3 + packages/ui/playwright.config.ts | 2 +- .../src/components/OsButton/OsButton.spec.ts | 92 ++- .../components/OsButton/OsButton.stories.ts | 284 ++++++-- .../OsButton/OsButton.visual.spec.ts | 97 ++- .../ui/src/components/OsButton/OsButton.vue | 132 ++-- .../chromium/all-appearances.png | Bin 0 -> 31688 bytes .../__screenshots__/chromium/all-sizes.png | Bin 8199 -> 48893 bytes .../__screenshots__/chromium/all-variants.png | Bin 8455 -> 9875 bytes .../chromium/appearance-filled.png | Bin 0 -> 9875 bytes .../chromium/appearance-ghost.png | Bin 0 -> 7796 bytes .../chromium/appearance-outline.png | Bin 0 -> 9803 bytes .../__screenshots__/chromium/danger.png | Bin 1851 -> 0 bytes .../__screenshots__/chromium/disabled.png | Bin 1870 -> 17003 bytes .../__screenshots__/chromium/full-width.png | Bin 2142 -> 11577 bytes .../__screenshots__/chromium/primary.png | Bin 1807 -> 0 bytes .../__screenshots__/chromium/secondary.png | Bin 2122 -> 0 bytes .../components/OsButton/button.variants.ts | 279 ++++++-- packages/ui/src/styles/index.css | 34 +- packages/ui/src/vite-env.d.ts | 2 + packages/ui/vite.config.ts | 3 + webapp/Dockerfile | 9 + webapp/Dockerfile.maintenance | 9 + .../_new/styles/ocelot-ui-variables.scss | 57 ++ webapp/components/CommentCard/CommentCard.vue | 23 +- .../ContributionForm/ContributionForm.vue | 18 +- .../components/DeleteData/DeleteData.spec.js | 28 +- webapp/components/DeleteData/DeleteData.vue | 10 +- .../DonationInfo/DonationInfo.spec.js | 14 +- .../components/DonationInfo/DonationInfo.vue | 6 +- webapp/components/Embed/EmbedComponent.vue | 26 +- webapp/components/EnterNonce/EnterNonce.vue | 7 +- webapp/components/Group/GroupForm.vue | 4 +- webapp/components/Group/GroupMember.vue | 11 +- webapp/components/LoginButton/LoginButton.vue | 2 +- webapp/components/Map/MapStylesButtons.vue | 37 +- webapp/components/Modal/ConfirmModal.vue | 4 +- webapp/components/Modal/DeleteUserModal.vue | 55 +- webapp/components/Modal/DisableModal.vue | 8 +- .../NotificationMenu/NotificationMenu.vue | 15 +- .../RegistrationSlideNoPublic.vue | 2 +- .../components/ReleaseModal/ReleaseModal.vue | 9 +- webapp/components/Uploader/ImageUploader.vue | 14 +- .../UserTeaser/UserTeaserPopover.vue | 4 +- .../MySomethingList/MySomethingList.vue | 14 +- .../features/ReportRow/ReportRow.vue | 14 +- webapp/jest.config.js | 24 +- webapp/nuxt.config.js | 16 + webapp/package.json | 2 + webapp/pages/admin/donations.vue | 6 +- webapp/pages/groups/create.vue | 2 +- webapp/pages/groups/edit/_id/index.vue | 2 +- webapp/pages/notifications/index.vue | 13 +- webapp/pages/post/_id/_slug/index.spec.js | 6 +- webapp/pages/post/_id/_slug/index.vue | 2 +- webapp/pages/profile/_id/_slug.vue | 30 +- .../__snapshots__/notifications.spec.js.snap | 12 +- webapp/pages/settings/badges.vue | 16 +- webapp/pages/settings/embeds.vue | 20 +- webapp/pages/settings/notifications.vue | 24 +- webapp/pages/settings/privacy.spec.js | 4 +- webapp/pages/settings/privacy.vue | 6 +- webapp/pages/terms-and-conditions-confirm.vue | 10 +- webapp/plugins/styleguide.js | 2 + webapp/test/__mocks__/@ocelot-social/ui.js | 83 +++ webapp/test/vueDemiSetup.js | 25 + webapp/yarn.lock | 30 + 83 files changed, 2496 insertions(+), 485 deletions(-) create mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/all-appearances.png create mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/appearance-filled.png create mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/appearance-ghost.png create mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/appearance-outline.png delete mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/danger.png delete mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/primary.png delete mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/secondary.png create mode 100644 webapp/assets/_new/styles/ocelot-ui-variables.scss create mode 100644 webapp/test/__mocks__/@ocelot-social/ui.js create mode 100644 webapp/test/vueDemiSetup.js diff --git a/.github/file-filters.yml b/.github/file-filters.yml index dd0e64fcc..cc249b86c 100644 --- a/.github/file-filters.yml +++ b/.github/file-filters.yml @@ -1,5 +1,9 @@ # These file filter patterns are used by the action https://github.com/dorny/paths-filter +ui: &ui + - '.github/workflows/ui-*.yml' + - 'packages/ui/**/*' + backend: &backend - '.github/workflows/test-backend.yml' - 'backend/**/*' @@ -14,6 +18,7 @@ webapp: &webapp - 'webapp/**/*' - 'styleguide/**/*' - 'package.json' + - *ui docs-check: &docs-check - '.github/workflows/check-documentation.yml' diff --git a/.github/workflows/ui-build.yml b/.github/workflows/ui-build.yml index bf134db5c..881893f8f 100644 --- a/.github/workflows/ui-build.yml +++ b/.github/workflows/ui-build.yml @@ -5,18 +5,38 @@ on: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-build.yml' pull_request: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-build.yml' defaults: run: working-directory: packages/ui jobs: + files-changed: + name: Detect File Changes + runs-on: ubuntu-latest + outputs: + ui: ${{ steps.changes.outputs.ui }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check for file changes + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + token: ${{ github.token }} + filters: .github/file-filters.yml + build: name: Build + if: needs.files-changed.outputs.ui == 'true' + needs: files-changed runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ui-compatibility.yml b/.github/workflows/ui-compatibility.yml index 9bc1080af..34b6ea883 100644 --- a/.github/workflows/ui-compatibility.yml +++ b/.github/workflows/ui-compatibility.yml @@ -5,14 +5,34 @@ on: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-compatibility.yml' pull_request: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-compatibility.yml' jobs: + files-changed: + name: Detect File Changes + runs-on: ubuntu-latest + outputs: + ui: ${{ steps.changes.outputs.ui }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check for file changes + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + token: ${{ github.token }} + filters: .github/file-filters.yml + build-library: name: Build Library + if: needs.files-changed.outputs.ui == 'true' + needs: files-changed runs-on: ubuntu-latest defaults: run: @@ -44,7 +64,8 @@ jobs: test-compatibility: name: Test ${{ matrix.example }} - needs: build-library + if: needs.files-changed.outputs.ui == 'true' + needs: [files-changed, build-library] runs-on: ubuntu-latest strategy: fail-fast: false diff --git a/.github/workflows/ui-docker.yml b/.github/workflows/ui-docker.yml index 2f783aeaa..fbe6c4ab4 100644 --- a/.github/workflows/ui-docker.yml +++ b/.github/workflows/ui-docker.yml @@ -5,14 +5,34 @@ on: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-docker.yml' pull_request: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-docker.yml' jobs: + files-changed: + name: Detect File Changes + runs-on: ubuntu-latest + outputs: + ui: ${{ steps.changes.outputs.ui }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check for file changes + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + token: ${{ github.token }} + filters: .github/file-filters.yml + build: name: Build Docker Image + if: needs.files-changed.outputs.ui == 'true' + needs: files-changed runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ui-lint.yml b/.github/workflows/ui-lint.yml index d0c78be44..85cdff7cc 100644 --- a/.github/workflows/ui-lint.yml +++ b/.github/workflows/ui-lint.yml @@ -5,18 +5,38 @@ on: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-lint.yml' pull_request: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-lint.yml' defaults: run: working-directory: packages/ui jobs: + files-changed: + name: Detect File Changes + runs-on: ubuntu-latest + outputs: + ui: ${{ steps.changes.outputs.ui }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check for file changes + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + token: ${{ github.token }} + filters: .github/file-filters.yml + lint: name: ESLint + if: needs.files-changed.outputs.ui == 'true' + needs: files-changed runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ui-size.yml b/.github/workflows/ui-size.yml index 45e73d803..1994e4a0b 100644 --- a/.github/workflows/ui-size.yml +++ b/.github/workflows/ui-size.yml @@ -5,18 +5,38 @@ on: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-size.yml' pull_request: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-size.yml' defaults: run: working-directory: packages/ui jobs: + files-changed: + name: Detect File Changes + runs-on: ubuntu-latest + outputs: + ui: ${{ steps.changes.outputs.ui }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check for file changes + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + token: ${{ github.token }} + filters: .github/file-filters.yml + size: name: Bundle Size Check + if: needs.files-changed.outputs.ui == 'true' + needs: files-changed runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ui-storybook.yml b/.github/workflows/ui-storybook.yml index b26f5c9dc..dec487bd6 100644 --- a/.github/workflows/ui-storybook.yml +++ b/.github/workflows/ui-storybook.yml @@ -5,18 +5,38 @@ on: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-storybook.yml' pull_request: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-storybook.yml' defaults: run: working-directory: packages/ui jobs: + files-changed: + name: Detect File Changes + runs-on: ubuntu-latest + outputs: + ui: ${{ steps.changes.outputs.ui }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check for file changes + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + token: ${{ github.token }} + filters: .github/file-filters.yml + build: name: Build Storybook + if: needs.files-changed.outputs.ui == 'true' + needs: files-changed runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ui-test.yml b/.github/workflows/ui-test.yml index 41e16518d..b8c6387e1 100644 --- a/.github/workflows/ui-test.yml +++ b/.github/workflows/ui-test.yml @@ -5,18 +5,38 @@ on: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-test.yml' pull_request: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-test.yml' defaults: run: working-directory: packages/ui jobs: + files-changed: + name: Detect File Changes + runs-on: ubuntu-latest + outputs: + ui: ${{ steps.changes.outputs.ui }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check for file changes + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + token: ${{ github.token }} + filters: .github/file-filters.yml + test: name: Unit Tests + if: needs.files-changed.outputs.ui == 'true' + needs: files-changed runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ui-verify.yml b/.github/workflows/ui-verify.yml index 0d594d79e..38a6b5af2 100644 --- a/.github/workflows/ui-verify.yml +++ b/.github/workflows/ui-verify.yml @@ -5,18 +5,38 @@ on: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-verify.yml' pull_request: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-verify.yml' defaults: run: working-directory: packages/ui jobs: + files-changed: + name: Detect File Changes + runs-on: ubuntu-latest + outputs: + ui: ${{ steps.changes.outputs.ui }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check for file changes + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + token: ${{ github.token }} + filters: .github/file-filters.yml + verify: name: Completeness Check + if: needs.files-changed.outputs.ui == 'true' + needs: files-changed runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ui-visual.yml b/.github/workflows/ui-visual.yml index af95e57ab..ce67621e0 100644 --- a/.github/workflows/ui-visual.yml +++ b/.github/workflows/ui-visual.yml @@ -5,18 +5,38 @@ on: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-visual.yml' pull_request: branches: [master] paths: - 'packages/ui/**' + - '.github/workflows/ui-visual.yml' defaults: run: working-directory: packages/ui jobs: + files-changed: + name: Detect File Changes + runs-on: ubuntu-latest + outputs: + ui: ${{ steps.changes.outputs.ui }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check for file changes + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + token: ${{ github.token }} + filters: .github/file-filters.yml + visual: name: Visual Regression + if: needs.files-changed.outputs.ui == 'true' + needs: files-changed runs-on: ubuntu-latest steps: diff --git a/packages/ui/.storybook/preview.ts b/packages/ui/.storybook/preview.ts index 6b57b34c3..7d0b4abc3 100644 --- a/packages/ui/.storybook/preview.ts +++ b/packages/ui/.storybook/preview.ts @@ -1,4 +1,6 @@ // eslint-disable-next-line import-x/no-unassigned-import +import '@fontsource-variable/inter' +// eslint-disable-next-line import-x/no-unassigned-import import './storybook.css' export const parameters = { diff --git a/packages/ui/.storybook/storybook.css b/packages/ui/.storybook/storybook.css index 854f50ae6..bbca58d0d 100644 --- a/packages/ui/.storybook/storybook.css +++ b/packages/ui/.storybook/storybook.css @@ -1,34 +1,54 @@ -/* Inter font for consistent rendering across platforms */ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); - @import "tailwindcss"; -/* Greyscale theme for Storybook - demonstrates that colors come from the app */ +/* Watercolor theme for Storybook - vibrant colors inspired by aquarelle paints */ /* All colors meet WCAG AA contrast requirements (4.5:1 for normal text) */ :root { - font-family: 'Inter', system-ui, sans-serif; + font-family: 'Inter Variable', system-ui, sans-serif; - --color-primary: #404040; - --color-primary-hover: #262626; + /* Default (Buff / Raw Umber) - neutral button */ + --color-default: #d8c9b8; + --color-default-hover: #c4b19c; + --color-default-active: #a89478; + --color-default-contrast: #3d2e1e; + --color-default-contrast-inverse: #ffffff; + + /* Primary (Ultramarine Blue) */ + --color-primary: #4a5a9e; + --color-primary-hover: #5e6db2; + --color-primary-active: #3b4882; --color-primary-contrast: #ffffff; - --color-secondary: #595959; - --color-secondary-hover: #404040; + /* Secondary (Dioxazine Violet) */ + --color-secondary: #7b519a; + --color-secondary-hover: #9068ad; + --color-secondary-active: #654280; --color-secondary-contrast: #ffffff; - --color-danger: #525252; - --color-danger-hover: #404040; + /* Danger (Alizarin Crimson) */ + --color-danger: #b83a4e; + --color-danger-hover: #cc5567; + --color-danger-active: #9a2e3f; --color-danger-contrast: #ffffff; - --color-warning: #d4d4d4; - --color-warning-hover: #a3a3a3; - --color-warning-contrast: #000000; + /* Warning (Yellow Ochre / Raw Sienna) - dark amber, works as text on white */ + --color-warning: #946b1d; + --color-warning-hover: #a87c28; + --color-warning-active: #7d5a15; + --color-warning-contrast: #ffffff; - --color-success: #525252; - --color-success-hover: #404040; + /* Success (Viridian Green) */ + --color-success: #3d7a5e; + --color-success-hover: #509470; + --color-success-active: #30644c; --color-success-contrast: #ffffff; - --color-info: #595959; - --color-info-hover: #404040; + /* Info (Cerulean Blue) */ + --color-info: #387598; + --color-info-hover: #4e8bae; + --color-info-active: #2d6080; --color-info-contrast: #ffffff; + + /* Disabled (Payne's Grey - muted watercolor grey) */ + --color-disabled: #c4bdb5; + --color-disabled-contrast: #5a4f45; } diff --git a/packages/ui/KATALOG.md b/packages/ui/KATALOG.md index 7b932a545..6a5381dd5 100644 --- a/packages/ui/KATALOG.md +++ b/packages/ui/KATALOG.md @@ -10,8 +10,9 @@ ### Übersicht ``` Phase 0: Analyse ██████████ 100% (8/8 Schritte) +Phase 3: Migration ████░░░░░░ 36% (32/90 Buttons) ─────────────────────────────────────────── -Nächste Phase: Phase 2 (Projekt-Setup) +Aktuelle Phase: Phase 3 - Milestone 4a ✅, nächster: Milestone 4b ``` ### Statistiken @@ -24,6 +25,109 @@ Nächste Phase: Phase 2 (Projekt-Setup) | Duplikate gefunden | 5 direkte + 3 Familien | | Zur Migration priorisiert | 15 Kern-Komponenten | +### OsButton Migration (Phase 3) +| Status | Anzahl | Details | +|--------|--------|---------| +| ✅ Migriert | 32 | Erste Welle (16) + Milestone 4a (14) + NotificationMenu (2) | +| ⏳ Ausstehend (mit neuen Props) | ~60 | Milestone 4c (benötigen icon/circle/loading) | +| **Gesamt** | **~90** | In ~50 Dateien | + +**Migrierte Komponenten (32):** + +*Erste Welle (16):* +- UserTeaserPopover.vue (1 Button) +- GroupForm.vue (1 Button - Cancel) +- EmbedComponent.vue (2 Buttons - Cancel, Play Now) +- DonationInfo.vue (1 Button) +- CommentCard.vue (1 Button - Show More) +- MapStylesButtons.vue (1 Button) +- GroupMember.vue (1 Button) +- embeds.vue (2 Buttons) +- notifications.vue (3 Buttons) +- privacy.vue (1 Button) +- terms-and-conditions-confirm.vue (2 Buttons) + +*Milestone 4a (14) ✅:* +- ✅ DisableModal.vue (1 Button - Cancel) +- ✅ DeleteUserModal.vue (1 Button - Cancel) +- ✅ ReleaseModal.vue (1 Button - Cancel) +- ✅ ContributionForm.vue (1 Button - Cancel) +- ✅ EnterNonce.vue (1 Button - Submit) +- ✅ MySomethingList.vue (1 Button - Cancel) +- ✅ ImageUploader.vue (2 Buttons - Crop) +- ✅ admin/donations.vue (1 Button - Save) +- ✅ profile/_id/_slug.vue (2 Buttons - Unblock, Unmute) +- ✅ settings/badges.vue (1 Button - Remove) +- ✅ notifications/index.vue (1 Button - Mark All Read) +- ✅ ReportRow.vue (1 Button - More Details) + +*Sonstige (2):* +- ✅ NotificationMenu.vue (2 Buttons - Mark All Read, Notification Page) + +**Ausstehend - benötigen neue Props (~60):** + +*Button-Komponenten mit icon/circle/loading:* +- ActionButton.vue: 3 Buttons (icon, circle) +- LabeledButton.vue: 1 Button (icon) +- MenuBarButton.vue: 1 Button (icon) +- EmotionButton.vue: 1 Button (icon) +- ShoutButton.vue: 1 Button (icon) +- FollowButton.vue: 1 Button (icon, loading) +- JoinLeaveButton.vue: 1 Button (icon, loading) +- ObserveButton.vue: 1 Button (icon, loading) +- InviteButton.vue: 1 Button (icon, loading) +- MapButton.vue: 1 Button (icon) +- PaginationButtons.vue: 2 Buttons (icon, circle) + +*Navigation mit icon:* +- LocaleSwitch.vue: 1 Button (icon) +- HeaderMenu.vue: 2 Buttons (icon) +- AvatarMenu.vue: 1 Button (icon, circle) +- NotificationMenu.vue: 1 Button (icon, circle) +- ChatNotificationMenu.vue: 1 Button (icon, circle) +- FilterMenu.vue: 1 Button (icon) + +*Editor-Buttons:* +- Editor.vue: ~10 Toolbar-Buttons (icon) +- ContextMenu.vue: 3 Buttons (icon) +- LinkInput.vue: 2 Buttons (icon, circle) + +*Filter-Buttons:* +- CategoriesFilter.vue: 1 Button (icon) +- HashtagsFilter.vue: 1 Button (icon) +- DropdownFilter.vue: 1 Button (icon) +- FilterMenuSection.vue: 2 Buttons (icon) +- DateTimeRange.vue: 2 Buttons (icon) + +*Chat-Buttons:* +- Chat.vue: 2 Buttons (icon) +- AddChatRoomByUserSearch.vue: 1 Button (icon, circle) + +*Form-Buttons:* +- CommentForm.vue: 1 Button (icon, loading) +- SearchField.vue: 1 Button (icon, circle) +- ShowPassword.vue: 1 Button (icon) + +*Modal-Buttons:* +- ConfirmModal.vue: 1 Button (loading) +- ReportModal.vue: 1 Button (loading) + +*Feature-Buttons:* +- CreateInvitation.vue: 1 Button (icon) +- Invitation.vue: 1 Button (icon, circle) +- SocialMediaListItem.vue: 1 Button (icon, circle) +- Request.vue: 1 Button (icon, loading) +- GroupButton.vue: 1 Button (icon) +- CtaJoinLeaveGroup.vue: 1 Button (icon) +- data-download.vue: 1 Button (icon, loading) +- AddGroupMember.vue: 1 Button (icon) +- GroupForm.vue Submit: 1 Button (icon) + +*Page-Buttons:* +- CommentCard.vue Reply: 1 Button (icon, circle) +- EmbedComponent.vue Close: 1 Button (icon, circle) +- PostTeaser.vue: 2 Buttons (icon) + --- ## Styleguide Komponenten (38) @@ -50,7 +154,7 @@ Nächste Phase: Phase 2 (Projekt-Setup) ### Data Input | # | Komponente | Status | Webapp-Duplikat | Varianten | Priorität | Notizen | |---|------------|--------|-----------------|-----------|-----------|---------| -| 13 | Button | ⬜ Ausstehend | BaseButton, CustomButton, ActionButton, ... | | | VIELE Varianten! | +| 13 | Button | ⏳ Migration | BaseButton, CustomButton, ActionButton, ... | | | → OsButton (16/90 migriert) | | 14 | CopyField | ⬜ Ausstehend | | | | | | 15 | Form | ⬜ Ausstehend | | | | | | 16 | FormItem | ⬜ Ausstehend | | | | | @@ -115,7 +219,7 @@ Nächste Phase: Phase 2 (Projekt-Setup) | 7 | BadgeSelection | ⬜ Ausstehend | Input | | | | 8 | Badges | ⬜ Ausstehend | Display | | | | 9 | BadgesSection | ⬜ Ausstehend | Display | | | -| 10 | BaseButton | ⬜ Ausstehend | Button | Button | 🔄 Button-Familie | +| 10 | BaseButton | ⏳ Migration | Button | Button | 🔄 → OsButton (16/90 migriert) | | 11 | BaseCard | ⬜ Ausstehend | Layout | Card | 🔗 DUPLIKAT | | 12 | BaseIcon | ⬜ Ausstehend | Display | Icon | 🔗 DUPLIKAT | @@ -129,7 +233,7 @@ Nächste Phase: Phase 2 (Projekt-Setup) | 17 | Change | ⬜ Ausstehend | Feature | | | | 18 | Chat | ⬜ Ausstehend | Feature | | Chat-spezifisch | | 19 | ChatNotificationMenu | ⬜ Ausstehend | Feature | | Chat-spezifisch | -| 20 | CommentCard | ⬜ Ausstehend | Display | Card | | +| 20 | CommentCard | ⏳ Teilweise | Display | Card | 1/2 Buttons → OsButton | | 21 | CommentForm | ⬜ Ausstehend | Input | Form | | | 22 | CommentList | ⬜ Ausstehend | Display | List | | | 23 | ComponentSlider | ⬜ Ausstehend | Layout | | | @@ -152,12 +256,12 @@ Nächste Phase: Phase 2 (Projekt-Setup) | 36 | DeleteData | ⬜ Ausstehend | Feature | | | | 37 | DeleteUserModal | ⬜ Ausstehend | Feedback | Modal | 🔄 Modal-Familie | | 38 | DisableModal | ⬜ Ausstehend | Feedback | Modal | 🔄 Modal-Familie | -| 39 | DonationInfo | ⬜ Ausstehend | Display | | | +| 39 | DonationInfo | ✅ Migriert | Display | | Button → OsButton | | 40 | Dropdown | ⬜ Ausstehend | Input | Select | | | 41 | DropdownFilter | ⬜ Ausstehend | Filter | Select | | | 42 | Editor | ⬜ Ausstehend | Input | | Rich-Text | | 43 | EmailDisplayAndVerify | ⬜ Ausstehend | Feature | | | -| 44 | EmbedComponent | ⬜ Ausstehend | Display | | | +| 44 | EmbedComponent | ⏳ Teilweise | Display | | 2/3 Buttons → OsButton | | 45 | EmotionButton | ⬜ Ausstehend | Button | Button | | | 46 | Emotions | ⬜ Ausstehend | Feature | | | | 47 | Empty | ⬜ Ausstehend | Feedback | Placeholder | | @@ -176,10 +280,10 @@ Nächste Phase: Phase 2 (Projekt-Setup) | 56 | FollowList | ⬜ Ausstehend | Display | List | | | 57 | GroupButton | ⬜ Ausstehend | Button | Button | | | 58 | GroupContentMenu | ⬜ Ausstehend | Navigation | Menu | | -| 59 | GroupForm | ⬜ Ausstehend | Input | Form | | +| 59 | GroupForm | ⏳ Teilweise | Input | Form | 1/2 Buttons → OsButton | | 60 | GroupLink | ⬜ Ausstehend | Navigation | | | | 61 | GroupList | ⬜ Ausstehend | Display | List | | -| 62 | GroupMember | ⬜ Ausstehend | Display | | | +| 62 | GroupMember | ✅ Migriert | Display | | Button → OsButton | | 63 | GroupTeaser | ⬜ Ausstehend | Display | Card | | ### H-L @@ -211,7 +315,7 @@ Nächste Phase: Phase 2 (Projekt-Setup) | # | Komponente | Status | Kategorie | Styleguide-Pendant | Notizen | |---|------------|--------|-----------|-------------------|---------| | 85 | MapButton | ⬜ Ausstehend | Button | Button | | -| 86 | MapStylesButtons | ⬜ Ausstehend | Button | Button | | +| 86 | MapStylesButtons | ✅ Migriert | Button | Button | Button → OsButton | | 87 | MasonryGrid | ⬜ Ausstehend | Layout | Grid | | | 88 | MasonryGridItem | ⬜ Ausstehend | Layout | GridItem | | | 89 | MenuBar | ⬜ Ausstehend | Navigation | Menu | | @@ -274,7 +378,7 @@ Nächste Phase: Phase 2 (Projekt-Setup) | 134 | UserTeaser | ⬜ Ausstehend | Display | Card | | | 135 | UserTeaserHelper | ⬜ Ausstehend | Display | | | | 136 | UserTeaserNonAnonymous | ⬜ Ausstehend | Display | | | -| 137 | UserTeaserPopover | ⬜ Ausstehend | Display | | | +| 137 | UserTeaserPopover | ✅ Migriert | Display | | Button → OsButton | --- @@ -308,7 +412,7 @@ Nächste Phase: Phase 2 (Projekt-Setup) | EmotionButton | Emotion | Feature-spezifisch | | JoinLeaveButton | Beitreten/Verlassen | Feature-spezifisch | | MapButton | Karten-Button | Feature-spezifisch | -| MapStylesButtons | Kartenstile | Feature-spezifisch | +| MapStylesButtons | Kartenstile | ✅ → OsButton | | CtaJoinLeaveGroup | CTA | Feature-spezifisch | | CtaUnblockAuthor | CTA | Feature-spezifisch | @@ -374,11 +478,18 @@ Diese sollten zuerst migriert werden: | 2026-02-04 | Claude | Priorisierung | 15 Komponenten in 4 Tiers priorisiert | | 2026-02-04 | Claude | Konsolidierungsplan | 3 Phasen definiert, Token-Liste erstellt | | 2026-02-04 | Claude | **Phase 0 abgeschlossen** | Bereit für Phase 2 (Projekt-Setup) | +| 2026-02-08 | Claude | OsButton entwickelt | CVA-Varianten, Vue 2/3 kompatibel via vue-demi | +| 2026-02-08 | Claude | Webapp-Integration | Jest Mock, Docker Build, CI-Kompatibilität | +| 2026-02-08 | Claude | **16 Buttons migriert** | Alle ohne icon/circle/loading Props, validiert | +| 2026-02-08 | Claude | OsButton erweitert | attrs/listeners Forwarding für Vue 2 ($listeners) | +| 2026-02-09 | Claude | Scope erweitert | ~90 Buttons identifiziert (16 migriert, 14 ohne Props, ~60 mit Props) | +| 2026-02-09 | Claude | **Milestone 4a: 8 Buttons** | DisableModal, DeleteUserModal, ReleaseModal, ContributionForm, EnterNonce, MySomethingList, ImageUploader (2x) | --- ## Nächste Schritte +### Phase 0: Analyse ✅ 1. [x] Webapp-Komponenten auflisten 2. [x] Styleguide-Komponenten auflisten 3. [x] Offensichtliche Duplikate identifizieren @@ -388,9 +499,33 @@ Diese sollten zuerst migriert werden: 7. [x] Priorisierung festlegen 8. [x] Konsolidierungsplan finalisieren +### Phase 3: OsButton Migration (in Arbeit) +9. [x] OsButton entwickeln (CVA, vue-demi) +10. [x] Webapp-Integration (Jest, Docker, CI) +11. [x] 16 Buttons migrieren (validiert ✅) + +**Milestone 4a: 14 Buttons ohne neue Props** +12. [ ] Modal Cancel-Buttons (3) +13. [ ] Form Cancel/Submit-Buttons (3) +14. [ ] ImageUploader Crop-Buttons (2) +15. [ ] Page Buttons (6) + +**Milestone 4b: Props für ~60 Buttons hinzufügen** +16. [ ] icon-Prop zu OsButton hinzufügen +17. [ ] circle-Variant zu OsButton hinzufügen +18. [ ] loading-Prop zu OsButton hinzufügen + +**Milestone 4c: ~60 Buttons mit neuen Props migrieren** +19. [ ] Button-Komponenten (~15) +20. [ ] Navigation (~8) +21. [ ] Editor (~15) +22. [ ] Filter/Chat (~10) +23. [ ] Forms/Modals (~5) +24. [ ] Features/Pages (~12) + --- -**✅ Phase 0 abgeschlossen!** Weiter mit Phase 2 (Projekt-Setup). +**✅ Phase 0 abgeschlossen!** Phase 3 zu 27% erledigt (24/90 Buttons migriert). Milestone 4a: 8/14 Buttons. --- @@ -1073,3 +1208,99 @@ $z-index-dropdown: 8888 $box-shadow-x-large: 0 15px 30px 0 rgba(0,0,0,.11), ... $box-shadow-small-inset: inset 0 0 0 1px rgba(0,0,0,.05) ``` + +--- + +## Phase 3: Webapp-Integration (Tracking) + +### OsButton Migration - Abgeschlossen (16/90) + +| # | Datei | Button | Status | +|---|-------|--------|--------| +| 1 | UserTeaserPopover.vue | Open Profile | ✅ Migriert | +| 2 | GroupForm.vue | Cancel | ✅ Migriert | +| 3 | EmbedComponent.vue | Cancel | ✅ Migriert | +| 4 | EmbedComponent.vue | Play Now | ✅ Migriert | +| 5 | DonationInfo.vue | Donate | ✅ Migriert | +| 6 | CommentCard.vue | Show More | ✅ Migriert | +| 7 | MapStylesButtons.vue | Style Toggle | ✅ Migriert | +| 8 | GroupMember.vue | Remove | ✅ Migriert | +| 9 | embeds.vue | Allow All | ✅ Migriert | +| 10 | embeds.vue | Deny All | ✅ Migriert | +| 11 | notifications.vue | Check All | ✅ Migriert | +| 12 | notifications.vue | Uncheck All | ✅ Migriert | +| 13 | notifications.vue | Save | ✅ Migriert | +| 14 | privacy.vue | Save | ✅ Migriert | +| 15 | terms-and-conditions-confirm.vue | Read T&C | ✅ Migriert | +| 16 | terms-and-conditions-confirm.vue | Save | ✅ Migriert | + +### OsButton Migration - Ausstehend ohne neue Props (Milestone 4a: 14/90) + +| # | Datei | Button | OsButton Props | Status | +|---|-------|--------|----------------|--------| +| 17 | Modal/DisableModal.vue | Cancel | `default` | ⬜ Ausstehend | +| 18 | Modal/DeleteUserModal.vue | Cancel | `default` | ⬜ Ausstehend | +| 19 | Modal/ReleaseModal.vue | Cancel | `default` | ⬜ Ausstehend | +| 20 | ContributionForm.vue | Cancel | `:disabled` | ⬜ Ausstehend | +| 21 | EnterNonce.vue | Submit | `variant="primary" :disabled` | ⬜ Ausstehend | +| 22 | MySomethingList.vue | Cancel | `default` | ⬜ Ausstehend | +| 23 | ImageUploader.vue | Crop Confirm 1 | `variant="primary"` | ⬜ Ausstehend | +| 24 | ImageUploader.vue | Crop Confirm 2 | `variant="primary"` | ⬜ Ausstehend | +| 25 | admin/donations.vue | Save | `variant="primary"` | ⬜ Ausstehend | +| 26 | profile/_id/_slug.vue | Unblock | `default` | ⬜ Ausstehend | +| 27 | profile/_id/_slug.vue | Unmute | `default` | ⬜ Ausstehend | +| 28 | settings/badges.vue | Remove | `default` | ⬜ Ausstehend | +| 29 | notifications/index.vue | Mark All Read | `variant="primary" :disabled` | ⬜ Ausstehend | +| 30 | ReportRow.vue | More Details | `size="sm"` | ⬜ Ausstehend | + +### OsButton Migration - Ausstehend mit neuen Props (Milestone 4c: ~60/90) + +> Diese Buttons benötigen icon, circle, und/oder loading Props. +> Siehe "Ausstehend - benötigen neue Props (~60)" oben für vollständige Liste. + +**Kategorien:** +| Kategorie | Anzahl | Props benötigt | +|-----------|--------|----------------| +| Button-Komponenten | ~15 | icon, circle, loading | +| Navigation | ~8 | icon, circle | +| Editor | ~15 | icon | +| Filter/Chat | ~10 | icon, circle | +| Forms/Modals | ~5 | icon, loading | +| Features/Pages | ~12 | icon, circle, loading | + +### Fehlende OsButton-Features + +| Feature | Benötigt für | Status | +|---------|-------------|--------| +| `icon` Prop | ~55 Buttons | ⬜ Fehlt | +| `circle` Variant | ~25 Buttons | ⬜ Fehlt | +| `loading` Prop | ~10 Buttons | ⬜ Fehlt | +| `appearance="outline"` | ✅ Implementiert | ✅ Erledigt | +| `appearance="ghost"` | ✅ Implementiert | ✅ Erledigt | + +### Nächste Schritte + +**Milestone 4a: 14 Buttons ohne neue Props migrieren** +1. Modal Cancel-Buttons (3) +2. Form Cancel/Submit-Buttons (3) +3. ImageUploader Crop-Buttons (2) +4. Page Buttons (6) + +**Milestone 4b: Props für ~60 Buttons hinzufügen** +1. Icon-Prop zu OsButton hinzufügen +2. Circle-Variant zu OsButton hinzufügen +3. Loading-Prop zu OsButton hinzufügen + +**Milestone 4c: ~60 Buttons mit neuen Props migrieren** +1. Button-Komponenten (~15) +2. Navigation (~8) +3. Editor (~15) +4. Filter/Chat (~10) +5. Forms/Modals (~5) +6. Features/Pages (~12) + +### Integrations-Protokoll + +| Datum | Aktion | Details | +|-------|--------|---------| +| 2026-02-08 | Analyse | 6 Einsatzstellen identifiziert, 2 minimal (nur variant) | diff --git a/packages/ui/PROJEKT.md b/packages/ui/PROJEKT.md index 2c40026c8..e110ff28e 100644 --- a/packages/ui/PROJEKT.md +++ b/packages/ui/PROJEKT.md @@ -51,6 +51,8 @@ | # | 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) | @@ -78,12 +80,11 @@ Phase 0: ██████████ 100% (6/6 Aufgaben) ✅ Phase 1: ██████████ 100% (6/6 Aufgaben) ✅ Phase 2: ██████████ 100% (26/26 Aufgaben) ✅ -Phase 3: ░░░░░░░░░░ 0% (0/7 Aufgaben) +Phase 3: █████████░ 83% (20/24 Aufgaben) - Webapp-Integration (M4a ✅, M5 ✅) Phase 4: █░░░░░░░░░ 6% (1/17 Aufgaben) - OsButton ✅ Phase 5: ░░░░░░░░░░ 0% (0/7 Aufgaben) -Webapp: ░░░░░░░░░░ 0% (0/1 Aufgaben) ─────────────────────────────────────── -Gesamt: █████░░░░░ 56% (39/70 Aufgaben) +Gesamt: ███████░░░ 69% (59/86 Aufgaben) ``` ### Katalogisierung (Details in KATALOG.md) @@ -93,22 +94,30 @@ Styleguide: ██████████ 100% (38 Komponenten erfasst) Analyse: ██████████ 100% (Button, Modal, Menu detailiert) ``` -### Komponenten-Migration (Priorisiert: 15) +### OsButton Migration (Phase 3) ``` -Analysiert: 3 Familien (Button, Modal, Menu) -Spezifiziert: 1 (OsButton) -Entwickelt: 1 (OsButton mit CVA) -QA bestanden: 1 (OsButton: 100% Coverage, Visual, A11y, Keyboard) -Integriert: 0 +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-08 +**Letzte Aktualisierung:** 2026-02-10 (Session 12) -**Aktuelle Phase:** Phase 3 (Token-System & Basis) - Bereit zum Start +**Aktuelle Phase:** Phase 3 (Webapp-Integration) - Milestone 4a abgeschlossen ✅ (32 Buttons migriert, nächster: Milestone 4b) **Zuletzt abgeschlossen:** - [x] Projektordner erstellt @@ -166,8 +175,8 @@ Integriert: 0 - 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 (Greyscale-Theme) - - OsButton.stories.ts mit allen Varianten + - 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 @@ -175,21 +184,91 @@ Integriert: 0 - ESLint Plugins: vuejs-accessibility, playwright, storybook, jsdoc **Aktuell in Arbeit:** -- Bereit für Phase 3: Token-System & Basis +- 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 - - [x] CSS Custom Properties Token-System aufsetzen - - [x] Storybook für Dokumentation einrichten - - [x] Docker Setup (Dockerfile, docker-compose, ui-docker.yml) - - [x] Visual Regression Tests (Playwright + @axe-core/playwright) - - [x] Keyboard Accessibility Tests - - [x] ESLint Plugins (storybook, playwright, vuejs-accessibility, jsdoc) - - [x] Storybook Build Workflow (ui-storybook.yml) - - [x] Completeness Check Script (verify: Story, Visual, checkA11y, Keyboard, Varianten) -4. **Phase 3: Token-System & Basis** - Nächste Phase +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) @@ -239,7 +318,7 @@ Integriert: 0 - [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 +- [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) @@ -252,14 +331,175 @@ Integriert: 0 - [x] CONTRIBUTING.md - [x] Completeness Check Script (Story, Visual+checkA11y, Keyboard, Varianten) -### Phase 3: Token-System & Basis -- [ ] Base Tokens definieren (Farben, Spacing, Typography) -- [ ] Semantic Tokens definieren -- [ ] Component Tokens definieren -- [ ] Branding-System implementieren (CSS Variables) -- [ ] Beispiel-Branding erstellen (Standard + Yunite) -- [ ] Storybook Theme-Farben anpassen (ocelot.social Branding) -- [ ] Token-Dokumentation in Storybook +### 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) @@ -1061,6 +1301,8 @@ Bei der Migration werden: | 66 | Branding-Hierarchie | Webapp → Spezialisiertes Branding | Default-Branding in Webapp, Overrides pro Instanz | | 67 | Variable-Validierung | Runtime-Check in Development | `validateCssVariables()` warnt bei fehlenden Variablen | | 68 | Branding-Test (Webapp) | CI-Test in Webapp | Webapp testet, dass Default-Branding alle Library-Variablen definiert | +| 69 | Webapp ↔ Maintenance Sharing | Webapp als Source of Truth | Kein separates "shared" Package, maintenance importiert aus webapp/ (siehe §16a) | +| 70 | Daten-Entkopplung | ViewModel/Mapper Pattern | Komponenten kennen nur ViewModels, Mapper transformieren API-Daten (siehe §16b) | ### Komponenten-API & Konventionen @@ -1185,6 +1427,65 @@ Bei der Migration werden: | 2026-02-08 | **Projekt-Optimierung** | src/test/setup.ts entfernt, @storybook/vue3 entfernt, README.md fix | | 2026-02-08 | **Package Updates** | size-limit 12.0.0, eslint-plugin-jsdoc 62.5.4, vite-tsconfig-paths 6.1.0 | | 2026-02-08 | **TODO: eslint-config-it4c** | Muss auf ESLint 10 aktualisiert werden (aktuell inkompatibel) | +| 2026-02-08 | **Phase 3: vue-demi Integration** | vue-demi zur Webapp hinzugefügt, Webpack-Alias für Vue 2.7 Kompatibilität | +| 2026-02-08 | **Phase 3: Webpack-Alias** | @ocelot-social/ui$ und style.css$ Aliase für yarn-linked Package | +| 2026-02-08 | **Phase 3: isVue2 Render** | OsButton mit isVue2 Check: Vue 2 attrs-Objekt, Vue 3 flat props | +| 2026-02-08 | **Phase 3: CSS-Spezifität** | UI-Library CSS nach Styleguide laden (styleguide.js Plugin) | +| 2026-02-08 | **Phase 3: Jest vue-demi** | Custom Mock (`__mocks__/@ocelot-social/ui.js`) mit Module._load Patch, defineComponent von Vue.default, vueDemiSetup.js, 979 Tests ✅ | +| 2026-02-08 | **Phase 3: Button-Styles** | Variants angepasst: font-semibold, rounded, box-shadow, h-[37.5px] | +| 2026-02-08 | **Phase 3: Erste Integration** | UserTeaserPopover.vue verwendet `` | +| 2026-02-08 | **Phase 3: Visueller Test** | Manueller Vergleich OsButton vs ds-button erfolgreich ✅ | +| 2026-02-08 | **Phase 3: v8 ignore** | Vue 2 Branch in OsButton mit `/* v8 ignore */` für 100% Coverage in Vitest | +| 2026-02-08 | **Phase 3: Docker Build** | ui-library Stage in Dockerfile + Dockerfile.maintenance, COPY --from=ui-library | +| 2026-02-08 | **Phase 3: CI-Fix** | Relativer Pfad `file:../packages/ui` statt absolut für yarn install außerhalb Docker | +| 2026-02-08 | **Phase 3: Storybook Fix** | TypeScript-Fehler in Stories behoben (`default` aus args entfernt) | +| 2026-02-08 | **Phase 3: attrs/listeners** | OsButton forwarded jetzt attrs + $listeners für Vue 2 (getCurrentInstance) | +| 2026-02-08 | **Phase 3: Jest Mock erweitert** | Alle Composition API Funktionen (computed, ref, watch, etc.) im Mock | +| 2026-02-08 | **Phase 3: 15 Buttons migriert** | GroupForm, EmbedComponent, DonationInfo, CommentCard, MapStylesButtons, GroupMember, embeds, notifications, privacy, terms-and-conditions-confirm | +| 2026-02-08 | **Phase 3: Test-Updates** | privacy.spec.js Selektoren, notifications Snapshot, DonationInfo.spec.js | +| 2026-02-08 | **OsButton: appearance Prop** | Neue `appearance` Prop: `filled` (default), `outline`, `ghost` - ermöglicht base-button Stile | +| 2026-02-08 | **OsButton: xs Size** | Exakte Pixel-Werte für base-button --small: h-26px, px-8px, text-12px, rounded-5px | +| 2026-02-08 | **OsButton: outline primary** | Grüner Rahmen + grüner Text + hellgrüner Hintergrund-Tint (rgba(25,122,49,0.18)) | +| 2026-02-08 | **OsButton: ghost primary** | Transparenter Hintergrund, grüner Text, Hover füllt grün, Active dunkler | +| 2026-02-08 | **OsButton: Focus Style** | `focus:outline-dashed focus:outline-1` statt ring (wie base-button) | +| 2026-02-08 | **OsButton: Active State** | `active:bg-[var(--color-*-hover)]` für dunkleren Hintergrund beim Drücken | +| 2026-02-08 | **Visuelle Validierung** | Tracking-Tabelle in PROJEKT.md für manuelle Button-Vergleiche (4/16 validiert) | +| 2026-02-08 | **Storybook Grayscale Theme** | Vollständige CSS-Variablen: default, active-states, contrast-inverse | +| 2026-02-08 | **Tailwind Source Filter** | `@import "tailwindcss" source(none)` - verhindert Markdown-Scanning | +| 2026-02-08 | **Button Variants Konsistenz** | Alle 21 compound variants mit korrekten active-states (`--color-*-active`) | +| 2026-02-08 | **CSS-Variablen erweitert** | `--color-secondary/warning/success/info-active` in ocelot-ui-variables.scss | +| 2026-02-08 | **Story Dokumentation** | "Medium (37.5px)" → "Medium (36px)" korrigiert | +| 2026-02-08 | **Playwright Toleranz** | `maxDiffPixelRatio: 0.03` für Cross-Platform Font-Rendering | +| 2026-02-09 | **Disabled-Styles korrigiert** | CSS-Variablen `--color-disabled`, filled: grauer Hintergrund statt opacity | +| 2026-02-09 | **terms-and-conditions-confirm** | Read T&C Button → `appearance="outline" variant="primary"` | +| 2026-02-09 | **Visuelle Validierung** | 10/16 Buttons validiert (terms-and-conditions-confirm.vue abgeschlossen) | +| 2026-02-09 | **Disabled:active/hover Fix** | CSS-Regeln in index.css mit höherer Spezifität für sofortige disabled-Darstellung | +| 2026-02-09 | **notifications.vue** | Check All + Uncheck All → `appearance="outline" variant="primary"` | +| 2026-02-09 | **Visuelle Validierung** | 14/16 Buttons validiert (notifications.vue abgeschlossen) | +| 2026-02-09 | **embeds.vue** | Allow All → `appearance="outline" variant="primary"` | +| 2026-02-09 | **Disabled Border-Fix** | CSS-Regeln in index.css: `border-style: solid` + `border-width: 0.8px` bei :disabled | +| 2026-02-09 | **Visuelle Validierung abgeschlossen** | 16/16 Buttons validiert (100%) ✅ Milestone 5 erfolgreich | +| 2026-02-09 | **Button-Analyse erweitert** | 14 weitere Buttons identifiziert (ohne icon/circle/loading) → Scope: 16/35 | +| 2026-02-09 | **Scope auf ~90 erweitert** | ~60 weitere Buttons mit icon/circle/loading identifiziert | +| 2026-02-09 | **Milestone 4a: 8 Buttons** | DisableModal, DeleteUserModal, ReleaseModal, ContributionForm, EnterNonce, MySomethingList, ImageUploader (2x) | +| 2026-02-09 | **ImageUploader CSS-Fix** | `position: absolute !important` für crop-confirm (überschreibt OsButton `relative`) | +| 2026-02-09 | **§16a hinzugefügt** | Webapp ↔ Maintenance Code-Sharing: Webapp als Source of Truth (Entscheidung #69) | +| 2026-02-09 | **§16b hinzugefügt** | Daten-Entkopplung: ViewModel/Mapper Pattern für API-agnostische Komponenten (Entscheidung #70) | +| 2026-02-09 | **NotificationMenu.vue** | 2 Buttons migriert (ghost primary), padding-top Fix für vertical-align Unterschied | +| 2026-02-09 | **Milestone 4a abgeschlossen** | 6 weitere Buttons migriert: donations.vue (Save), profile/_id/_slug.vue (Unblock, Unmute), badges.vue (Remove), notifications/index.vue (Mark All Read), ReportRow.vue (More Details) | +| 2026-02-10 | **Wasserfarben-Farbschema** | Greyscale-Theme → Aquarell-Farben (Ultramarin, Dioxazin-Violett, Alizarin, Ocker, Viridian, Cöruleum), WCAG AA konform | +| 2026-02-10 | **Stories konsolidiert** | Primary/Secondary/Danger/Default entfernt → AllVariants; AllSizes/AllAppearances/Disabled/FullWidth zeigen alle 7 Varianten | +| 2026-02-10 | **Appearance: Filled/Outline/Ghost** | Einzelne Stories umbenannt und mit allen 7 Varianten erweitert | +| 2026-02-10 | **Playground-Story** | Interaktive Controls (argTypes nur in Playground, nicht global) | +| 2026-02-10 | **Einheitlicher Border** | `border-[0.8px] border-solid border-transparent` als Base-Klasse für alle Appearances | +| 2026-02-10 | **WCAG 2.4.7 Fix** | Default-Variante: `focus:outline-none` → `focus:outline-dashed focus:outline-current` | +| 2026-02-10 | **Keyboard A11y Test** | Playwright-Test fokussiert alle Buttons und prüft `outlineStyle !== 'none'` | +| 2026-02-10 | **data-appearance Attribut** | OsButton rendert `data-appearance` auf `