Skip to content

Commit ce83407

Browse files
authored
fix(checkbox): re-evaluate label visibility when label is updated (#30980)
## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> Checkbox's render function applies the `label-text-wrapper-hidden` css class when there is no label text to prevent extra margin from being added. The render function is not re-evaluated if the label is updated. This causes a problem in Angular where dynamic variables get applied after the page is loaded, and a checkbox using a variable as a label gets stuck with its label hidden until something else triggers a re-render, e.g. ticking the box. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> - The checkbox will be re-rendered, and css classes will be updated, when the label text is changed. - Updated tests to check that the label is visible after changing from blank to having content. ## Does this introduce a breaking change? - [ ] Yes - [X] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. -->
1 parent 784fdc6 commit ce83407

File tree

4 files changed

+26
-9
lines changed

4 files changed

+26
-9
lines changed

core/src/components/checkbox/checkbox.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ export class Checkbox implements ComponentInterface {
127127
*/
128128
@State() isInvalid = false;
129129

130+
@State() private hasLabelContent = false;
131+
130132
@State() private hintTextId?: string;
131133

132134
/**
@@ -265,6 +267,10 @@ export class Checkbox implements ComponentInterface {
265267
ev.stopPropagation();
266268
};
267269

270+
private onSlotChange = () => {
271+
this.hasLabelContent = this.el.textContent !== '';
272+
};
273+
268274
private getHintTextId(): string | undefined {
269275
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
270276

@@ -326,7 +332,6 @@ export class Checkbox implements ComponentInterface {
326332
} = this;
327333
const mode = getIonMode(this);
328334
const path = getSVGPath(mode, indeterminate);
329-
const hasLabelContent = el.textContent !== '';
330335

331336
renderHiddenInput(true, el, name, checked ? value : '', disabled);
332337

@@ -338,7 +343,7 @@ export class Checkbox implements ComponentInterface {
338343
aria-checked={indeterminate ? 'mixed' : `${checked}`}
339344
aria-describedby={this.hintTextId}
340345
aria-invalid={this.isInvalid ? 'true' : undefined}
341-
aria-labelledby={hasLabelContent ? this.inputLabelId : null}
346+
aria-labelledby={this.hasLabelContent ? this.inputLabelId : null}
342347
aria-label={inheritedAttributes['aria-label'] || null}
343348
aria-disabled={disabled ? 'true' : null}
344349
aria-required={required ? 'true' : undefined}
@@ -376,13 +381,13 @@ export class Checkbox implements ComponentInterface {
376381
<div
377382
class={{
378383
'label-text-wrapper': true,
379-
'label-text-wrapper-hidden': !hasLabelContent,
384+
'label-text-wrapper-hidden': !this.hasLabelContent,
380385
}}
381386
part="label"
382387
id={this.inputLabelId}
383388
onClick={this.onDivLabelClick}
384389
>
385-
<slot></slot>
390+
<slot onSlotchange={this.onSlotChange}></slot>
386391
{this.renderHintText()}
387392
</div>
388393
<div class="native-wrapper">

packages/angular/test/base/e2e/src/standalone/value-accessors.spec.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,19 @@ test.describe('Value Accessors', () => {
88

99
test('should update the form value', async ({ page }) => {
1010
await expect(page.locator('#formValue')).toHaveText(JSON.stringify({ checkbox: false }, null, 2));
11-
await expect(page.locator('ion-checkbox')).toHaveClass(/ion-pristine/);
11+
await expect(page.getByRole('checkbox', { name: 'Static Checkbox Label' })).toHaveClass(/ion-pristine/);
1212

13-
await page.locator('ion-checkbox').click();
13+
await page.getByRole('checkbox', { name: 'Static Checkbox Label' }).click();
1414

1515
await expect(page.locator('#formValue')).toHaveText(JSON.stringify({ checkbox: true }, null, 2));
16-
await expect(page.locator('ion-checkbox')).toHaveClass(/ion-dirty/);
17-
await expect(page.locator('ion-checkbox')).toHaveClass(/ion-valid/);
16+
await expect(page.getByRole('checkbox', { name: 'Static Checkbox Label' })).toHaveClass(/ion-dirty/);
17+
await expect(page.getByRole('checkbox', { name: 'Static Checkbox Label' })).toHaveClass(/ion-valid/);
18+
19+
await expect(page.getByRole('checkbox', { name: 'Static Checkbox Label' })).toBeVisible();
20+
});
21+
22+
test('should display dynamically set label', async ({ page }) => {
23+
await expect(page.getByRole('checkbox', { name: 'Dynamic Checkbox Label' })).toBeVisible();
1824
});
1925
});
2026

packages/angular/test/base/src/app/standalone/value-accessors/checkbox/checkbox.component.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ <h1>IonCheckbox Value Accessors</h1>
66
</p>
77

88
<app-value-accessor-test [formGroup]="form">
9-
<ion-checkbox formControlName="checkbox">Checkbox</ion-checkbox>
9+
<ion-checkbox formControlName="checkbox">Static Checkbox Label</ion-checkbox>
1010
</app-value-accessor-test>
11+
<ion-checkbox>{{dynamicLabel}}</ion-checkbox>
1112
</div>

packages/angular/test/base/src/app/standalone/value-accessors/checkbox/checkbox.component.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ import { ValueAccessorTestComponent } from "../value-accessor-test/value-accesso
1515
]
1616
})
1717
export class CheckboxComponent {
18+
dynamicLabel = '';
19+
20+
ngAfterViewInit(): void {
21+
this.dynamicLabel = 'Dynamic Checkbox Label';
22+
}
1823

1924
form = this.fb.group({
2025
checkbox: [false, Validators.required],

0 commit comments

Comments
 (0)