Skip to content

fix(ZMSKVR-1228): keyboard usable modal#1933

Open
ChristianVierthaler wants to merge 10 commits intonextfrom
bugfix-zmskvr-1228-keyboard-usable-modal
Open

fix(ZMSKVR-1228): keyboard usable modal#1933
ChristianVierthaler wants to merge 10 commits intonextfrom
bugfix-zmskvr-1228-keyboard-usable-modal

Conversation

@ChristianVierthaler
Copy link
Contributor

@ChristianVierthaler ChristianVierthaler commented Feb 18, 2026

Pull Request Checklist (Feature Branch to next):

  • Ich habe die neuesten Änderungen aus dem next Branch in meinen Feature-Branch gemergt.
  • Das Code-Review wurde abgeschlossen.
  • Fachliche Tests wurden durchgeführt und sind abgeschlossen.

Summary by CodeRabbit

  • Refactor

    • Modal replaced with a slot-based pattern and switched from a show/close API to a v-model-style open/update:open binding for visibility.
  • New Features

    • Modal now uses pattern library modal/button components and accepts a translation prop for localized labels.
  • Tests

    • Unit tests updated to use the new open/update:open API and assert actual visibility.
  • Chores

    • Test setup adds DOM mocks for modal show/close methods; config trailing blank lines removed.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 18, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors AvailabilityInfoModal to use MucModal with v-model (open / update:open) instead of the show prop and close event; updates its consumer, unit tests, and test setup (adds HTMLElement showModal/close mocks) to match the slot-based modal API.

Changes

Cohort / File(s) Summary
Modal component & consumer
zmscitizenview/src/components/Appointment/AppointmentSelection.vue, zmscitizenview/src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue
Replaced custom modal markup with MucModal/MucButton; prop renamed showopen (v-model:open); emits changed from closeupdate:open (boolean); header/body/footer moved to named slots; removed old internal styles and close-event handling; consumer now binds :t="t".
Unit tests for modal
zmscitizenview/tests/unit/Appointment/AppointmentSelection.spec.ts, zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts
Tests updated to mount/use open and expect update:open emissions (boolean); assertions switched to visibility checks (isVisible()); selectors updated for slot markup and .modal-content/.standard-headline; ARIA text normalized; added/updated stubs for MucModal/MucButton.
Test setup / mocks
zmscitizenview/tests/setup/localStorageMock.ts
Added vi import; directly instantiates LocalStorageMock() when needed; when window is defined, adds HTMLElement.prototype.showModal and HTMLElement.prototype.close as vi.fn() to support modal interactions in tests.
Config whitespace
zmscitizenview/vitest.config.ts
Removed trailing blank lines; no functional change.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User
participant AppointmentSelection
participant AvailabilityInfoModal
participant MucModal
Note over AppointmentSelection,AvailabilityInfoModal: Parent controls modal via v-model:open
User->>AppointmentSelection: Click "Info"
AppointmentSelection->>AvailabilityInfoModal: set open = true
AvailabilityInfoModal->>MucModal: pass :open="open" (slot content)
MucModal-->>AvailabilityInfoModal: user closes modal (internal)
MucModal->>AvailabilityInfoModal: emit update:open false
AvailabilityInfoModal->>AppointmentSelection: v-model updates open = false

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I swapped my show for open and hopped into a slot,
Muc-modal shelters messages where old divs used to trot,
Tests learn to listen for update:open with cheer,
Mocked showModal and close keep every run clear,
I twitch a whisker — the dialog closes, no fear.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(ZMSKVR-1228): keyboard usable modal' directly and specifically references the main change: converting a modal component to be keyboard-navigable using the muc-modal component with v-model binding instead of manual prop control.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix-zmskvr-1228-keyboard-usable-modal

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (4)
zmscitizenview/src/components/Appointment/AppointmentSelection.vue (1)

677-680: closeAvailabilityInfoModal is now dead code — remove it.

With @close="closeAvailabilityInfoModal" removed from the template, this function has no callers. The v-model binding handles showAvailabilityInfoModal.value = false, and availabilityInfoHtmlOverride is already cleared on the next call to openAvailabilityInfoModal. Safe to delete.

🧹 Proposed fix
-const closeAvailabilityInfoModal = () => {
-  showAvailabilityInfoModal.value = false;
-  availabilityInfoHtmlOverride.value = "";
-};
-
 const availabilityInfoHtml = computed(() => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@zmscitizenview/src/components/Appointment/AppointmentSelection.vue` around
lines 677 - 680, The function closeAvailabilityInfoModal is unused/dead code —
remove the entire function definition (const closeAvailabilityInfoModal = () =>
{ ... }) from AppointmentSelection.vue; ensure there are no remaining references
to closeAvailabilityInfoModal elsewhere (the template now uses v-model on
showAvailabilityInfoModal and openAvailabilityInfoModal already clears
availabilityInfoHtmlOverride), so deleting this declaration is safe.
zmscitizenview/tests/setup/localStorageMock.ts (1)

43-55: Patching HTMLElement.prototype is overly broad — target HTMLDialogElement.prototype instead.

showModal and close are native HTMLDialogElement methods. Attaching them to HTMLElement.prototype injects the mocks on every element in the DOM during tests. Since setup files run inside the configured jsdom environment, HTMLDialogElement is available and should be the target:

-if (typeof window !== 'undefined') {
-  Object.defineProperty(HTMLElement.prototype, 'showModal', {
+if (typeof window !== 'undefined' && typeof HTMLDialogElement !== 'undefined') {
+  Object.defineProperty(HTMLDialogElement.prototype, 'showModal', {
     configurable: true,
     writable: true,
     value: vi.fn(),
   });

-  Object.defineProperty(HTMLElement.prototype, 'close', {
+  Object.defineProperty(HTMLDialogElement.prototype, 'close', {
     configurable: true,
     writable: true,
     value: vi.fn(),
   });
 }

Also note that vi.fn() instances are created once at module initialisation and shared across all tests in a worker. If individual tests assert call counts on these mocks, state can leak between tests. Consider either calling vi.fn() inside a beforeEach reset or using vi.spyOn per test.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@zmscitizenview/tests/setup/localStorageMock.ts` around lines 43 - 55, The
test setup incorrectly patches HTMLElement.prototype for the dialog APIs; change
the mock target to HTMLDialogElement.prototype and replace the module-level
vi.fn() assignments for showModal and close with per-test setup—either create
the vi.fn() mocks inside a beforeEach (or reset them in beforeEach) or prefer
using vi.spyOn on HTMLDialogElement.prototype per test—so the symbols to update
are HTMLElement.prototype -> HTMLDialogElement.prototype, the mocked methods
showModal and close, and the vi.fn() usage (move into beforeEach or switch to
vi.spyOn) to avoid cross-test state leakage.
zmscitizenview/src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue (1)

38-44: dialogAriaLabel prop is declared but never used — remove it.

The prop dialogAriaLabel (line 42) has no reference in the template; accessibility labelling is handled via aria-labelledby="modalTitle" on <muc-modal>. Keeping unused props widens the public API surface unnecessarily.

🧹 Proposed fix
 const props = defineProps<{
   open: boolean;
   html: string;
   closeAriaLabel?: string;
-  dialogAriaLabel?: string;
   t?: (key: string) => string;
 }>();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@zmscitizenview/src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue`
around lines 38 - 44, Remove the unused dialogAriaLabel prop from the
component's props definition to shrink the public API: edit the defineProps
block in AvailabilityInfoModal.vue and delete the dialogAriaLabel?: string;
entry (leaving open, html, closeAriaLabel, t unchanged), then search the
component for any references to dialogAriaLabel and remove them (there should be
none; aria labeling is handled via aria-labelledby="modalTitle" on <muc-modal>),
and update any related TypeScript types or external references if they exist.
zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts (1)

26-27: Remove noisy inline comment from stub template

The comment <!-- props direkt verwenden, nicht props.closeAriaLabel --> is implementation noise embedded inside a template string — it explains a workaround without adding value to someone reading the test later. As per coding guidelines, comments should be avoided when the code is self-explanatory, and closing/noise comments should be removed.

♻️ Proposed fix
-             <!-- props direkt verwenden, nicht props.closeAriaLabel -->
              <button class="modal-button-close" :aria-label="closeAriaLabel || 'Dialog schließen'" type="button"></button>

As per coding guidelines, "Don't add obvious noise" and "Always try to explain yourself in code."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts`
around lines 26 - 27, Remove the noisy inline HTML comment <!-- props direkt
verwenden, nicht props.closeAriaLabel --> from the stub/template used in the
AvailabilityInfoModal unit test; locate the template string or stub in
AvailabilityInfoModal.spec.ts (the test's stub/template for the modal) and
delete that comment so the template is clean and self-explanatory, leaving the
surrounding markup and any props references (e.g., props.closeAriaLabel)
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@zmscitizenview/src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue`:
- Around line 2-7: The template binds v-model:open to props.open which attempts
to assign into a readonly prop; change to a writable proxy: create a writable
computed (e.g., const computedOpen = computed({ get: () => props.open, set: v =>
emit('update:open', v) }) in the <script setup>) and use
v-model:open="computedOpen" on <muc-modal>, or alternatively bind
:open="props.open" and `@update`:open="val => emit('update:open', val)"; update
references to props.open accordingly so MucModal emits correctly and no readonly
assignment occurs.

In `@zmscitizenview/tests/unit/Appointment/AppointmentSelection.spec.ts`:
- Line 1816: The test is setting a non-existent prop with wrapper.setProps({
open: true }) which leaves the modal closed; instead set the component's local
ref directly by assigning wrapper.vm.showAvailabilityInfoModal = true and then
await nextTick() to ensure reactivity updates, and where you do use
wrapper.setProps(...) elsewhere (e.g., the other occurrence noted) make it
awaited (await wrapper.setProps({...})) rather than fire-and-forget to ensure
the DOM updates before assertions.

In
`@zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts`:
- Around line 85-87: The test named 'has modal-dialog with centered class' is
stale because it only checks for '.modal-content'; either rename the test to
reflect that assertion or update the assertions to actually verify the modal
structure: locate the test with the string 'has modal-dialog with centered
class' in AvailabilityInfoModal.spec.ts and replace its body to expect a
'.modal-dialog' element and that it contains the expected centered class (e.g.,
'.modal-dialog.centered' or the actual class used by AvailabilityInfoModal), or
change the test name to e.g. 'renders modal content' to match the existing
expect(wrapper.find(".modal-content").exists()).
- Around line 17-53: The MucModal stub in AvailabilityInfoModal.spec.ts declares
the open prop but ignores it, so the modal always renders; update the MucModal
stub template to conditionally render its content based on the open prop (e.g.,
wrap the modal-content template in a v-if="open" or equivalent) so prop-driven
visibility behaves like the real component, and then change the "does not render
when open is false" test to assert the modal is not visible/not present (use
wrapper.find('.modal-content').exists() or isVisible()) instead of merely
checking wrapper.props('open') === false; ensure the "renders when open is true"
test still checks visibility.
- Around line 122-129: The test is self-fulfilling because it directly emits the
event instead of wiring the stub's close button to trigger the component's
update:open handler; update the test for AvailabilityInfoModal so the stubbed
close button actually emits 'update:open' with false when clicked (i.e.,
configure the stub to call wrapper.vm.$emit('update:open', false) on its click
or use Vue Test Utils' listeners to forward the event), remove the manual await
wrapper.vm.$emit('update:open', false) call, trigger
closeButton.trigger('click'), await nextTick/DOM updates, and then assert that
wrapper emitted('update:open') with payload [false]; also rename the test
description from "emits close event" to "emits update:open with false when close
clicked".

In `@zmscitizenview/vitest.config.ts`:
- Around line 5-13: Remove the unreachable browser-only block starting with "if
(typeof window !== \"undefined\") { ... }" in vitest.config.ts (which defines
HTMLDialogElement and calls showModal/close via vi.fn()) because Vitest loads
this file in Node where window is undefined and the mocks are already
implemented in localStorageMock.ts; delete that entire block and any dangling
references to vi in this file (or add a proper import if vi is needed elsewhere)
so the config no longer contains dead code or an undeclared vi reference.

---

Nitpick comments:
In `@zmscitizenview/src/components/Appointment/AppointmentSelection.vue`:
- Around line 677-680: The function closeAvailabilityInfoModal is unused/dead
code — remove the entire function definition (const closeAvailabilityInfoModal =
() => { ... }) from AppointmentSelection.vue; ensure there are no remaining
references to closeAvailabilityInfoModal elsewhere (the template now uses
v-model on showAvailabilityInfoModal and openAvailabilityInfoModal already
clears availabilityInfoHtmlOverride), so deleting this declaration is safe.

In
`@zmscitizenview/src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue`:
- Around line 38-44: Remove the unused dialogAriaLabel prop from the component's
props definition to shrink the public API: edit the defineProps block in
AvailabilityInfoModal.vue and delete the dialogAriaLabel?: string; entry
(leaving open, html, closeAriaLabel, t unchanged), then search the component for
any references to dialogAriaLabel and remove them (there should be none; aria
labeling is handled via aria-labelledby="modalTitle" on <muc-modal>), and update
any related TypeScript types or external references if they exist.

In `@zmscitizenview/tests/setup/localStorageMock.ts`:
- Around line 43-55: The test setup incorrectly patches HTMLElement.prototype
for the dialog APIs; change the mock target to HTMLDialogElement.prototype and
replace the module-level vi.fn() assignments for showModal and close with
per-test setup—either create the vi.fn() mocks inside a beforeEach (or reset
them in beforeEach) or prefer using vi.spyOn on HTMLDialogElement.prototype per
test—so the symbols to update are HTMLElement.prototype ->
HTMLDialogElement.prototype, the mocked methods showModal and close, and the
vi.fn() usage (move into beforeEach or switch to vi.spyOn) to avoid cross-test
state leakage.

In
`@zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts`:
- Around line 26-27: Remove the noisy inline HTML comment <!-- props direkt
verwenden, nicht props.closeAriaLabel --> from the stub/template used in the
AvailabilityInfoModal unit test; locate the template string or stub in
AvailabilityInfoModal.spec.ts (the test's stub/template for the modal) and
delete that comment so the template is clean and self-explanatory, leaving the
surrounding markup and any props references (e.g., props.closeAriaLabel)
unchanged.

Comment on lines +2 to 7
<muc-modal
v-model:open="props.open"
@close="emit('update:open', false)"
aria-labelledby="modalTitle"
:closeAriaLabel="closeAriaLabel || 'Dialog schließen'"
>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

v-model:open="props.open" mutates a readonly prop — Vue 3 will warn and the update may silently fail.

v-model:open compiles to :open="props.open" plus @update:open="props.open = $event". In Vue 3's Composition API, props is a shallow readonly proxy, so the assignment triggers a runtime warning (Set operation on key "open": target is readonly) and the value is not written. If MucModal emits update:open internally (e.g., when the user presses Escape), those updates are dropped, potentially breaking the keyboard-close flow that this PR is specifically fixing.

The idiomatic fix is a writable computed ref:

🔧 Proposed fix — writable computed for v-model passthrough

In <script setup>:

+import { computed } from "vue";
 import { computed } from "vue";

 ...

+const isOpen = computed({
+  get: () => props.open,
+  set: (val: boolean) => emit('update:open', val),
+});

In <template>:

-  v-model:open="props.open"
-  `@close`="emit('update:open', false)"
+  v-model:open="isOpen"
+  `@close`="isOpen = false"

Or, without a computed, split binding and handler explicitly:

-  v-model:open="props.open"
-  `@close`="emit('update:open', false)"
+  :open="props.open"
+  `@update`:open="emit('update:open', $event)"
+  `@close`="emit('update:open', false)"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@zmscitizenview/src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue`
around lines 2 - 7, The template binds v-model:open to props.open which attempts
to assign into a readonly prop; change to a writable proxy: create a writable
computed (e.g., const computedOpen = computed({ get: () => props.open, set: v =>
emit('update:open', v) }) in the <script setup>) and use
v-model:open="computedOpen" on <muc-modal>, or alternatively bind
:open="props.open" and `@update`:open="val => emit('update:open', val)"; update
references to props.open accordingly so MucModal emits correctly and no readonly
assignment occurs.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts (1)

23-24: Redundant v-show on .modal-content — the outer v-if already gates the whole subtree

When open is false, the v-if="open" on the outer .modal div prevents the entire subtree from being mounted; .modal-content never enters the DOM and the v-show="open" on it is therefore dead code. When open is true, v-show="open" is always true and has no effect.

♻️ Proposed simplification
- <div class="modal-content" v-show="open">
+ <div class="modal-content">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts`
around lines 23 - 24, Remove the redundant v-show on the `.modal-content`
element since the outer `<div class="modal" role="dialog" aria-modal="true"
v-if="open">` already controls mounting; locate the `<div class="modal-content"
v-show="open">` (in AvailabilityInfoModal template) and delete the v-show="open"
attribute so the markup becomes `<div class="modal-content">`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts`:
- Line 28: Fix the broken `@click` expression and remove the $parent anti-pattern:
update the button in the stub (the element using `@click` that currently reads
"$emit('update:open', false; $parent.$emit('update:open', false)") to simply
emit the event itself (call $emit('update:open', false)) so there's no
semicolon-in-argument syntax error and no $parent access; also remove the
redundant v-show="open" from the .modal-content div since the parent .modal uses
v-if="open". After these changes the test can rely on
wrapper.emitted('update:open') to observe the stub emission.

---

Nitpick comments:
In
`@zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts`:
- Around line 23-24: Remove the redundant v-show on the `.modal-content` element
since the outer `<div class="modal" role="dialog" aria-modal="true"
v-if="open">` already controls mounting; locate the `<div class="modal-content"
v-show="open">` (in AvailabilityInfoModal template) and delete the v-show="open"
attribute so the markup becomes `<div class="modal-content">`.

<div class="modal-header">
<h2 class="standard-headline"><slot name="title"/></h2>
<!-- props direkt verwenden, nicht props.closeAriaLabel -->
<button class="modal-button-close" :aria-label="closeAriaLabel || 'Dialog schließen'" type="button" @click="$emit('update:open', false; $parent.$emit('update:open', false)"></button>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find the AvailabilityInfoModal.vue component file
fd "AvailabilityInfoModal.vue" -type f

Repository: it-at-m/eappointment

Length of output: 234


🏁 Script executed:

#!/bin/bash
# Search for defineModel, defineEmits, or emit patterns related to update:open
rg "defineModel|defineEmits|emit.*update:open" --type vue --type ts

Repository: it-at-m/eappointment

Length of output: 91


🏁 Script executed:

#!/bin/bash
# Examine the test file to confirm the syntax error on line 28
fd "AvailabilityInfoModal.spec.ts" --exec head -n 35 {} \; | tail -n 20

Repository: it-at-m/eappointment

Length of output: 1090


🏁 Script executed:

#!/bin/bash
# Find the AvailabilityInfoModal.vue component (not the .spec.ts file)
fd "AvailabilityInfoModal" -type f | grep -v "spec.ts"

Repository: it-at-m/eappointment

Length of output: 234


🏁 Script executed:

#!/bin/bash
# Search for defineModel, defineEmits patterns in the entire codebase
rg "defineModel|defineEmits" -l

Repository: it-at-m/eappointment

Length of output: 1039


🏁 Script executed:

#!/bin/bash
# Check the test lines 123-130 to see the event test
fd "AvailabilityInfoModal.spec.ts" --exec sed -n '118,130p' {}

Repository: it-at-m/eappointment

Length of output: 522


🏁 Script executed:

#!/bin/bash
# Inspect the actual AvailabilityInfoModal.vue component
cat -n zmscitizenview/src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue | head -n 80

Repository: it-at-m/eappointment

Length of output: 1608


🏁 Script executed:

#!/bin/bash
# Search for how the component handles update:open and MucModal
rg -A 5 -B 5 "update:open|MucModal" zmscitizenview/src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue

Repository: it-at-m/eappointment

Length of output: 968


Syntax error in @click handler will crash the template compiler, and $parent.$emit is an anti-pattern

The attribute value @click="$emit('update:open', false; $parent.$emit('update:open', false))" embeds a ; inside a function-call argument list — that is invalid JavaScript. Vue's template compiler wraps the expression in an arrow function, so this generates a SyntaxError at runtime and the entire stub (and therefore every test) will fail.

Beyond the syntax issue, reaching into $parent to fire events is a Vue 3 anti-pattern: it creates hidden coupling between the stub and the component under test and will silently break whenever the component tree changes.

The correct stub pattern is to emit update:open from the stub itself:

- <button class="modal-button-close" :aria-label="closeAriaLabel || 'Dialog schließen'" type="button" `@click`="$emit('update:open', false; $parent.$emit('update:open', false)"></button>
+ <button class="modal-button-close" :aria-label="closeAriaLabel || 'Dialog schließen'" type="button" `@click`="$emit('update:open', false)"></button>

Additionally, the .modal-content div (line 23-24) has a redundant v-show="open". Since the parent .modal already uses v-if="open", the entire child tree is removed when open=false, making the v-show directive unreachable dead code.

- <div class="modal-content" v-show="open">
+ <div class="modal-content">

wrapper.emitted('update:open') (line 127) will then correctly capture emissions from the stub.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button class="modal-button-close" :aria-label="closeAriaLabel || 'Dialog schließen'" type="button" @click="$emit('update:open', false; $parent.$emit('update:open', false)"></button>
MucModal: {
props: ['open', 'closeAriaLabel', 'dialogAriaLabel'],
emits: ['update:open'],
template: `
<div class="modal" role="dialog" aria-modal="true" v-if="open">
<div class="modal-content">
<div class="modal-header">
<h2 class="standard-headline"><slot name="title"/></h2>
<button class="modal-button-close" :aria-label="closeAriaLabel || 'Dialog schließen'" type="button" `@click`="$emit('update:open', false)"></button>
</div>
<div class="modal-body"><slot name="body"/></div>
<div class="modal-footer"><slot name="footer"/></div>
</div>
<div class="modal-backdrop"></div>
</div>
`,
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts`
at line 28, Fix the broken `@click` expression and remove the $parent
anti-pattern: update the button in the stub (the element using `@click` that
currently reads "$emit('update:open', false; $parent.$emit('update:open',
false)") to simply emit the event itself (call $emit('update:open', false)) so
there's no semicolon-in-argument syntax error and no $parent access; also remove
the redundant v-show="open" from the .modal-content div since the parent .modal
uses v-if="open". After these changes the test can rely on
wrapper.emitted('update:open') to observe the stub emission.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts`:
- Line 24: The v-show="open" on the .modal-content element is redundant because
the parent .modal already uses v-if="open", so remove the v-show="open"
attribute from the .modal-content element (look for the .modal and
.modal-content elements in the AvailabilityInfoModal component/test fixture) to
clean up dead code and avoid duplicate visibility directives.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@zmscitizenview/src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue`:
- Around line 2-6: The `@close` handler currently forwards the native CloseEvent
via emit('update:open', $event) which violates the component's emit type (e:
"update:open", value: boolean). Replace that usage so the muc-modal `@close`
handler emits a boolean (use emit('update:open', false)) to match the emit
signature and the footer button behavior; update the handler that references
emit('update:open', $event) to emit('update:open', false).

In
`@zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts`:
- Around line 17-35: The test stub for MucModal emits update:open on the close
button but the component under test listens for a close event, so change the
stub or the component to match: either update the stub's emitted event from
"update:open" to "close" (so the component's `@close` handler is triggered) or
change the component to listen for `@update`:open instead of `@close`; also remove
the redundant v-show="open" inside the stub since the outer v-if="open" already
controls rendering. Reference the MucModal stub, the button emitting
$emit('update:open', false), and the component's `@close/`@update:open listener
when making the change.

Comment on lines +2 to +6
<muc-modal
:open="open"
@close="emit('update:open', $event)"
aria-labelledby="modalTitle"
:closeAriaLabel="closeAriaLabel || 'Dialog schließen'"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd zmscitizenview && find . -name "*.vue" -o -name "*.ts" -o -name "*.js" | grep -i modal | head -20

Repository: it-at-m/eappointment

Length of output: 215


🏁 Script executed:

# Check the specific component file being reviewed
cd zmscitizenview && cat -n src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue | head -60

Repository: it-at-m/eappointment

Length of output: 1596


🏁 Script executed:

# Look for MucModal imports and dependencies
cd zmscitizenview && rg -n "MucModal" --type vue --type ts -A 3 src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue

Repository: it-at-m/eappointment

Length of output: 91


🏁 Script executed:

# Search for MucModal component definition in the project
cd zmscitizenview && fd -e vue -e ts -i mucmodal -x grep -l ".*" {}

Repository: it-at-m/eappointment

Length of output: 46


🏁 Script executed:

# Check node_modules for `@muenchen` package
cd zmscitizenview && ls -la node_modules/@muenchen/ 2>/dev/null | head -20 || echo "Directory not found"

Repository: it-at-m/eappointment

Length of output: 46


🏁 Script executed:

# Check package.json for dependencies
cd zmscitizenview && cat package.json | grep -A 10 -B 2 "muenchen\|dependency"

Repository: it-at-m/eappointment

Length of output: 1188


🏁 Script executed:

cd zmscitizenview && cat -n tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts | head -150

Repository: it-at-m/eappointment

Length of output: 6845


🏁 Script executed:

# Search for MucModal in node_modules
cd zmscitizenview && find node_modules/@muenchen -name "*modal*" -o -name "*Modal*" 2>/dev/null | head -10

Repository: it-at-m/eappointment

Length of output: 46


🏁 Script executed:

# Check the muc-patternlab-vue package for component definitions
cd zmscitizenview && find node_modules/@muenchen/muc-patternlab-vue -type f \( -name "*.d.ts" -o -name "*.vue" \) 2>/dev/null | grep -i modal

Repository: it-at-m/eappointment

Length of output: 46


🏁 Script executed:

# Look for package.json of muc-patternlab-vue to find entry point
cd zmscitizenview && cat node_modules/@muenchen/muc-patternlab-vue/package.json 2>/dev/null | head -50

Repository: it-at-m/eappointment

Length of output: 46


🏁 Script executed:

# Search for Modal component export or definition in the library
cd zmscitizenview && rg -l "MucModal|defineEmits.*close" node_modules/@muenchen/muc-patternlab-vue 2>/dev/null | head -10

Repository: it-at-m/eappointment

Length of output: 46


🏁 Script executed:

cd zmscitizenview && cat -n tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts | sed -n '117,131p'

Repository: it-at-m/eappointment

Length of output: 629


🏁 Script executed:

# Check if there are any other MucModal usages in the codebase to see expected patterns
cd zmscitizenview && rg -n "muc-modal\|MucModal" --type vue -B 2 -A 2 src/ | grep -E "(muc-modal|@close|@update:open)" | head -30

Repository: it-at-m/eappointment

Length of output: 91


🏁 Script executed:

# Look for documentation or type definitions of MucModal in node_modules
cd zmscitizenview && find node_modules/@muenchen -name "*.d.ts" 2>/dev/null | xargs grep -l "Modal" | head -5

Repository: it-at-m/eappointment

Length of output: 46


🏁 Script executed:

# Try to extract type information about MucModal's emits
cd zmscitizenview && find node_modules/@muenchen/muc-patternlab-vue -type f 2>/dev/null | xargs cat 2>/dev/null | grep -A 5 -B 5 "close\|Modal" | head -60

Repository: it-at-m/eappointment

Length of output: 46


🏁 Script executed:

cd zmscitizenview && rg -n "@close|@update:open" --type vue src/components/ | grep -v ".spec.ts" | head -20

Repository: it-at-m/eappointment

Length of output: 91


🏁 Script executed:

# Check if there are any other components using muc-modal to see expected pattern
cd zmscitizenview && rg -B 5 -A 5 "muc-modal" src/ --type vue | head -80

Repository: it-at-m/eappointment

Length of output: 91


🏁 Script executed:

# Let me search for any actual usages or patterns of muc-modal 
cd zmscitizenview && find . -name "*.vue" -type f -exec grep -l "muc-modal" {} \; 2>/dev/null

Repository: it-at-m/eappointment

Length of output: 200


🏁 Script executed:

# Check if node_modules has the actual compiled component 
cd zmscitizenview && ls -la node_modules/@muenchen/muc-patternlab-vue/ 2>/dev/null

Repository: it-at-m/eappointment

Length of output: 46


🏁 Script executed:

# Try to find any index file or main export from the library
cd zmscitizenview && cat node_modules/@muenchen/muc-patternlab-vue/package.json 2>/dev/null | grep -E "(main|exports|module|types)"

Repository: it-at-m/eappointment

Length of output: 46


🏁 Script executed:

# Let's check the other file that uses muc-modal to understand the pattern
cd zmscitizenview && cat -n src/components/AppointmentDetail/AppointmentDetailView.vue | grep -A 10 -B 5 "muc-modal"

Repository: it-at-m/eappointment

Length of output: 1928


🏁 Script executed:

# Try to find any configuration or type stubs for the library
cd zmscitizenview && find . -name "*.d.ts" -o -name "vite-env.d.ts" | xargs cat 2>/dev/null | grep -i modal

Repository: it-at-m/eappointment

Length of output: 46


🏁 Script executed:

# Check if there's a documentation or README that explains the MucModal API
cd zmscitizenview && find . -name "README*" -o -name "*.md" | xargs grep -l -i "muc-modal\|mucmodal" 2>/dev/null

Repository: it-at-m/eappointment

Length of output: 48


🏁 Script executed:

# Let me check if there's any error or failure log in the test
cd zmscitizenview && cat tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts | tail -30

Repository: it-at-m/eappointment

Length of output: 1299


🏁 Script executed:

# Check the complete test file to see if there are more tests after line 150
cd zmscitizenview && wc -l tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts

Repository: it-at-m/eappointment

Length of output: 141


🏁 Script executed:

# Look for any GitHub CI or test configuration that might show actual failures
cd zmscitizenview && find . -name "*.yml" -o -name "*.yaml" | xargs grep -l "test\|spec" 2>/dev/null | head -5

Repository: it-at-m/eappointment

Length of output: 46


🏁 Script executed:

# Let me understand the test flow - check if the test is actually testing the `@close` handler
cd zmscitizenview && cat -n tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts | sed -n '118,131p'

Repository: it-at-m/eappointment

Length of output: 621


🏁 Script executed:

# Check the stub's emits more carefully - does it have a way to trigger `@close`?
cd zmscitizenview && grep -n "emits\|close\|update:open" tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts

Repository: it-at-m/eappointment

Length of output: 1007


🏁 Script executed:

# Let me trace through what happens - check if there's any other event emission in the component
cd zmscitizenview && cat -n src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue | sed -n '1,30p'

Repository: it-at-m/eappointment

Length of output: 956


🏁 Script executed:

# The key question: when the stub's close button emits 'update:open', does the component catch it?
# Let's check if the component has both `@close` and anything that catches update:open from the modal
cd zmscitizenview && grep -n "update:open\|@close" src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue

Repository: it-at-m/eappointment

Length of output: 200


🏁 Script executed:

# Check if there's a v-model or two-way binding being used with the modal
cd zmscitizenview && cat -n src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue | sed -n '1,10p'

Repository: it-at-m/eappointment

Length of output: 367


Use explicit false instead of $event in the @close handler — it violates the emit type contract

The handler @close="emit('update:open', $event)" passes a native CloseEvent object, but the emit definition (line 47) expects a boolean:

(e: "update:open", value: boolean): void;

When MucModal fires its close event, $event is a browser Event object, not false. This breaks the type contract. The footer button correctly uses false (line 24), so align the close handler:

Fix
  <muc-modal
    :open="open"
-   `@close`="emit('update:open', $event)"
+   `@close`="emit('update:open', false)"
    aria-labelledby="modalTitle"
    :closeAriaLabel="closeAriaLabel || 'Dialog schließen'"

The test doesn't catch this because the stub only emits update:open (not close), so the real MucModal's close event behavior is untested.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@zmscitizenview/src/components/Appointment/AppointmentSelection/AvailabilityInfoModal.vue`
around lines 2 - 6, The `@close` handler currently forwards the native CloseEvent
via emit('update:open', $event) which violates the component's emit type (e:
"update:open", value: boolean). Replace that usage so the muc-modal `@close`
handler emits a boolean (use emit('update:open', false)) to match the emit
signature and the footer button behavior; update the handler that references
emit('update:open', $event) to emit('update:open', false).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts (1)

28-28: Remove noisy implementation reminder comment

The comment <!-- props direkt verwenden, nicht props.closeAriaLabel --> documents Vue's template prop access — this is standard Vue 3 knowledge and adds no informational value for a reader of the test.

🧹 Proposed fix
- <!-- props direkt verwenden, nicht props.closeAriaLabel -->

As per coding guidelines: "Don't add obvious noise" and "Don't be redundant" (Comments rules).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts`
at line 28, Remove the noisy HTML comment "<!-- props direkt verwenden, nicht
props.closeAriaLabel -->" from the AvailabilityInfoModal.spec.ts test file;
simply delete that redundant template comment (it references standard Vue prop
access) so the test code and template remain unchanged but without the
unnecessary reminder.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts`:
- Line 25: The v-show="open" on the .modal-content element is redundant because
the parent .modal already uses v-if="open", so remove the v-show="open"
attribute from the .modal-content element (the element shown as <div
class="modal-content" v-show="open">) to eliminate dead code and avoid
confusion; keep the parent .modal's v-if="open" intact.
- Around line 124-131: In the test stub for the AvailabilityInfoModal (the
component stub where the parent wrapper uses v-if="open"), remove the redundant
v-show="open" attribute from the nested element (since v-if already controls
presence) and delete the German explanatory comment that sits next to the prop
usage; update only the stub markup so it no longer contains the dead v-show and
the unnecessary comment, leaving the prop bindings and emitted behavior
unchanged (look for the stub definition near the top of
AvailabilityInfoModal.spec.ts where v-if="open" and v-show="open" are both
present).

---

Nitpick comments:
In
`@zmscitizenview/tests/unit/Appointment/AppointmentSelection/AvailabilityInfoModal.spec.ts`:
- Line 28: Remove the noisy HTML comment "<!-- props direkt verwenden, nicht
props.closeAriaLabel -->" from the AvailabilityInfoModal.spec.ts test file;
simply delete that redundant template comment (it references standard Vue prop
access) so the test code and template remain unchanged but without the
unnecessary reminder.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant