diff --git a/core/src/components/checkbox/checkbox.tsx b/core/src/components/checkbox/checkbox.tsx index 8a25b9200d5..569f31ab6ea 100644 --- a/core/src/components/checkbox/checkbox.tsx +++ b/core/src/components/checkbox/checkbox.tsx @@ -151,44 +151,53 @@ 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(() => { - 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(); - }); + this.validationObserver = new MutationObserver((mutations) => { + // Watch for label content changes + if (mutations.some((mutation) => mutation.type === 'characterData')) { + this.hasLabelContent = this.el.textContent !== ''; + } + // 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(); + }); + } } }); 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 +276,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 +392,7 @@ export class Checkbox implements ComponentInterface { id={this.inputLabelId} onClick={this.onDivLabelClick} > - + {this.renderHintText()}