From 9ac94a7c792738bb3190568f2c54b131a8cd5e6d Mon Sep 17 00:00:00 2001 From: Jacob Bell <228905018+OS-jacobbell@users.noreply.github.com> Date: Thu, 2 Apr 2026 14:18:42 -0600 Subject: [PATCH 1/2] fix(checkbox): use mutation observer instead of onslotchange --- core/src/components/checkbox/checkbox.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/src/components/checkbox/checkbox.tsx b/core/src/components/checkbox/checkbox.tsx index 8a25b9200d5..e18adeb6b49 100644 --- a/core/src/components/checkbox/checkbox.tsx +++ b/core/src/components/checkbox/checkbox.tsx @@ -153,7 +153,10 @@ export class Checkbox implements ComponentInterface { // Watch for class changes to update validation state. if (Build.isBrowser && typeof MutationObserver !== 'undefined') { - this.validationObserver = new MutationObserver(() => { + this.validationObserver = new MutationObserver((mutations) => { + if (mutations.some((mutation) => mutation.type === 'characterData')) { + this.hasLabelContent = this.el.textContent !== ''; + } const newIsInvalid = checkInvalidState(el); if (this.isInvalid !== newIsInvalid) { this.isInvalid = newIsInvalid; @@ -184,11 +187,14 @@ export class Checkbox implements ComponentInterface { this.validationObserver.observe(el, { attributes: true, attributeFilter: ['class'], + characterData: true, + subtree: true, }); } // Always set initial state this.isInvalid = checkInvalidState(el); + this.hasLabelContent = this.el.textContent !== ''; } componentWillLoad() { @@ -267,10 +273,6 @@ export class Checkbox implements ComponentInterface { ev.stopPropagation(); }; - private onSlotChange = () => { - this.hasLabelContent = this.el.textContent !== ''; - }; - private getHintTextId(): string | undefined { const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this; @@ -387,7 +389,7 @@ export class Checkbox implements ComponentInterface { id={this.inputLabelId} onClick={this.onDivLabelClick} > - + {this.renderHintText()}
From cba086a1d8fb5e2af5f67a4d1b97d84fb8f4c5a2 Mon Sep 17 00:00:00 2001 From: Jacob Bell <228905018+OS-jacobbell@users.noreply.github.com> Date: Fri, 3 Apr 2026 14:10:27 -0600 Subject: [PATCH 2/2] fix(checkbox): check if mutation is on attributes before updating validity --- core/src/components/checkbox/checkbox.tsx | 53 ++++++++++++----------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/core/src/components/checkbox/checkbox.tsx b/core/src/components/checkbox/checkbox.tsx index e18adeb6b49..569f31ab6ea 100644 --- a/core/src/components/checkbox/checkbox.tsx +++ b/core/src/components/checkbox/checkbox.tsx @@ -151,36 +151,39 @@ export class Checkbox implements ComponentInterface { connectedCallback() { const { el } = this; - // Watch for class changes to update validation state. if (Build.isBrowser && typeof MutationObserver !== 'undefined') { this.validationObserver = new MutationObserver((mutations) => { + // Watch for label content changes if (mutations.some((mutation) => mutation.type === 'characterData')) { this.hasLabelContent = this.el.textContent !== ''; } - const newIsInvalid = checkInvalidState(el); - if (this.isInvalid !== newIsInvalid) { - this.isInvalid = newIsInvalid; - /** - * Screen readers tend to announce changes - * to `aria-describedby` when the attribute - * is changed during a blur event for a - * native form control. - * However, the announcement can be spotty - * when using a non-native form control - * and `forceUpdate()`. - * This is due to `forceUpdate()` internally - * rescheduling the DOM update to a lower - * priority queue regardless if it's called - * inside a Promise or not, thus causing - * the screen reader to potentially miss the - * change. - * By using a State variable inside a Promise, - * it guarantees a re-render immediately at - * a higher priority. - */ - Promise.resolve().then(() => { - this.hintTextId = this.getHintTextId(); - }); + // Watch for class changes to update validation state. + if (mutations.some((mutation) => mutation.type === 'attributes')) { + const newIsInvalid = checkInvalidState(el); + if (this.isInvalid !== newIsInvalid) { + this.isInvalid = newIsInvalid; + /** + * Screen readers tend to announce changes + * to `aria-describedby` when the attribute + * is changed during a blur event for a + * native form control. + * However, the announcement can be spotty + * when using a non-native form control + * and `forceUpdate()`. + * This is due to `forceUpdate()` internally + * rescheduling the DOM update to a lower + * priority queue regardless if it's called + * inside a Promise or not, thus causing + * the screen reader to potentially miss the + * change. + * By using a State variable inside a Promise, + * it guarantees a re-render immediately at + * a higher priority. + */ + Promise.resolve().then(() => { + this.hintTextId = this.getHintTextId(); + }); + } } });