From 40620005db80454cfb1f4e5467fb10707a1f6182 Mon Sep 17 00:00:00 2001 From: Olympe Lespagnon Date: Wed, 22 Jul 2026 11:18:17 +0200 Subject: [PATCH] modifier/required-input: add cleanup and deduplication logic --- addon/modifiers/required-input.ts | 17 ++++++++++++++++- .../integration/helpers/required-input-test.ts | 17 +++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/addon/modifiers/required-input.ts b/addon/modifiers/required-input.ts index feca29311..06fc82940 100644 --- a/addon/modifiers/required-input.ts +++ b/addon/modifiers/required-input.ts @@ -1,4 +1,5 @@ -import Modifier from 'ember-modifier'; +import Modifier, { type ArgsFor } from 'ember-modifier'; +import { registerDestructor } from '@ember/destroyable'; interface RequiredInputSignature { Element: HTMLElement; @@ -6,14 +7,28 @@ interface RequiredInputSignature { export default class RegisterFormField extends Modifier { declare targetElement: HTMLElement; + markerElement?: HTMLSpanElement; + + constructor(owner: unknown, args: ArgsFor) { + super(owner, args); + registerDestructor(this, () => this.cleanup()); + } modify(element: HTMLElement): void { this.targetElement = element; + this.cleanup(); + const span = document.createElement('span'); span.classList.add('font-color-error-500'); span.textContent = '*'; + this.markerElement = span; element.appendChild(span); } + + private cleanup(): void { + this.markerElement?.remove(); + this.markerElement = undefined; + } } diff --git a/tests/integration/helpers/required-input-test.ts b/tests/integration/helpers/required-input-test.ts index 0039d3f5f..38ef262b9 100644 --- a/tests/integration/helpers/required-input-test.ts +++ b/tests/integration/helpers/required-input-test.ts @@ -47,4 +47,21 @@ module('Integration | Helper | required-input', function (hooks) { assert.dom('label span.existing.font-color-error-500').doesNotExist(); assert.dom('label span.font-color-error-500').exists(); }); + + test('it keeps one asterisk when the modifier is removed and re-added in the same render cycle', async function (assert) { + this.isRequired = true; + + await render(hbs` + + `); + assert.dom('label span.font-color-error-500').exists({ count: 1 }); + + this.set('isRequired', false); + await settled(); + assert.dom('label span.font-color-error-500').doesNotExist(); + + this.set('isRequired', true); + await settled(); + assert.dom('label span.font-color-error-500').exists({ count: 1 }); + }); });