mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
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. -->
This commit is contained in:
@@ -127,6 +127,8 @@ export class Checkbox implements ComponentInterface {
|
||||
*/
|
||||
@State() isInvalid = false;
|
||||
|
||||
@State() private hasLabelContent = false;
|
||||
|
||||
@State() private hintTextId?: string;
|
||||
|
||||
/**
|
||||
@@ -265,6 +267,10 @@ 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;
|
||||
|
||||
@@ -326,7 +332,6 @@ export class Checkbox implements ComponentInterface {
|
||||
} = this;
|
||||
const mode = getIonMode(this);
|
||||
const path = getSVGPath(mode, indeterminate);
|
||||
const hasLabelContent = el.textContent !== '';
|
||||
|
||||
renderHiddenInput(true, el, name, checked ? value : '', disabled);
|
||||
|
||||
@@ -338,7 +343,7 @@ export class Checkbox implements ComponentInterface {
|
||||
aria-checked={indeterminate ? 'mixed' : `${checked}`}
|
||||
aria-describedby={this.hintTextId}
|
||||
aria-invalid={this.isInvalid ? 'true' : undefined}
|
||||
aria-labelledby={hasLabelContent ? this.inputLabelId : null}
|
||||
aria-labelledby={this.hasLabelContent ? this.inputLabelId : null}
|
||||
aria-label={inheritedAttributes['aria-label'] || null}
|
||||
aria-disabled={disabled ? 'true' : null}
|
||||
aria-required={required ? 'true' : undefined}
|
||||
@@ -376,13 +381,13 @@ export class Checkbox implements ComponentInterface {
|
||||
<div
|
||||
class={{
|
||||
'label-text-wrapper': true,
|
||||
'label-text-wrapper-hidden': !hasLabelContent,
|
||||
'label-text-wrapper-hidden': !this.hasLabelContent,
|
||||
}}
|
||||
part="label"
|
||||
id={this.inputLabelId}
|
||||
onClick={this.onDivLabelClick}
|
||||
>
|
||||
<slot></slot>
|
||||
<slot onSlotchange={this.onSlotChange}></slot>
|
||||
{this.renderHintText()}
|
||||
</div>
|
||||
<div class="native-wrapper">
|
||||
|
||||
@@ -8,13 +8,19 @@ test.describe('Value Accessors', () => {
|
||||
|
||||
test('should update the form value', async ({ page }) => {
|
||||
await expect(page.locator('#formValue')).toHaveText(JSON.stringify({ checkbox: false }, null, 2));
|
||||
await expect(page.locator('ion-checkbox')).toHaveClass(/ion-pristine/);
|
||||
await expect(page.getByRole('checkbox', { name: 'Static Checkbox Label' })).toHaveClass(/ion-pristine/);
|
||||
|
||||
await page.locator('ion-checkbox').click();
|
||||
await page.getByRole('checkbox', { name: 'Static Checkbox Label' }).click();
|
||||
|
||||
await expect(page.locator('#formValue')).toHaveText(JSON.stringify({ checkbox: true }, null, 2));
|
||||
await expect(page.locator('ion-checkbox')).toHaveClass(/ion-dirty/);
|
||||
await expect(page.locator('ion-checkbox')).toHaveClass(/ion-valid/);
|
||||
await expect(page.getByRole('checkbox', { name: 'Static Checkbox Label' })).toHaveClass(/ion-dirty/);
|
||||
await expect(page.getByRole('checkbox', { name: 'Static Checkbox Label' })).toHaveClass(/ion-valid/);
|
||||
|
||||
await expect(page.getByRole('checkbox', { name: 'Static Checkbox Label' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should display dynamically set label', async ({ page }) => {
|
||||
await expect(page.getByRole('checkbox', { name: 'Dynamic Checkbox Label' })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
</p>
|
||||
|
||||
<app-value-accessor-test [formGroup]="form">
|
||||
<ion-checkbox formControlName="checkbox">Checkbox</ion-checkbox>
|
||||
<ion-checkbox formControlName="checkbox">Static Checkbox Label</ion-checkbox>
|
||||
</app-value-accessor-test>
|
||||
<ion-checkbox>{{dynamicLabel}}</ion-checkbox>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,11 @@ import { ValueAccessorTestComponent } from "../value-accessor-test/value-accesso
|
||||
]
|
||||
})
|
||||
export class CheckboxComponent {
|
||||
dynamicLabel = '';
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.dynamicLabel = 'Dynamic Checkbox Label';
|
||||
}
|
||||
|
||||
form = this.fb.group({
|
||||
checkbox: [false, Validators.required],
|
||||
|
||||
Reference in New Issue
Block a user