diff --git a/libs/components/src/checkbox/checkbox.component.ts b/libs/components/src/checkbox/checkbox.component.ts index b5d53c41fd6..d8df53943f3 100644 --- a/libs/components/src/checkbox/checkbox.component.ts +++ b/libs/components/src/checkbox/checkbox.component.ts @@ -1,4 +1,4 @@ -import { Component, HostBinding, Input, Optional, Self } from "@angular/core"; +import { booleanAttribute, Component, HostBinding, input, Optional, Self } from "@angular/core"; import { NgControl, Validators } from "@angular/forms"; import { BitFormControlAbstraction } from "../form-control"; @@ -7,6 +7,9 @@ import { BitFormControlAbstraction } from "../form-control"; selector: "input[type=checkbox][bitCheckbox]", template: "", providers: [{ provide: BitFormControlAbstraction, useExisting: CheckboxComponent }], + host: { + "[disabled]": "disabled", + }, }) export class CheckboxComponent implements BitFormControlAbstraction { @HostBinding("class") @@ -105,30 +108,17 @@ export class CheckboxComponent implements BitFormControlAbstraction { protected indeterminateImage = `url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none" viewBox="0 0 13 13"%3E%3Cpath stroke="%23fff" stroke-width="2" d="M2.5 6.5h8"/%3E%3C/svg%3E%0A')`; - // TODO: Skipped for signal migration because: - // Accessor inputs cannot be migrated as they are too complex. - @HostBinding() - @Input() - get disabled() { - return this._disabled ?? this.ngControl?.disabled ?? false; - } - set disabled(value: any) { - this._disabled = value != null && value !== false; - } - private _disabled?: boolean; + readonly disabledInput = input(false, { transform: booleanAttribute, alias: "disabled" }); + + // TODO migrate to computed signal when Angular adds signal support to reactive forms + // https://bitwarden.atlassian.net/browse/CL-819 + get disabled() { + return this.disabledInput() || this.ngControl?.disabled || false; + } - // TODO: Skipped for signal migration because: - // Accessor inputs cannot be migrated as they are too complex. - @Input() get required() { - return ( - this._required ?? this.ngControl?.control?.hasValidator(Validators.requiredTrue) ?? false - ); + return this.ngControl?.control?.hasValidator(Validators.requiredTrue) ?? false; } - set required(value: any) { - this._required = value != null && value !== false; - } - private _required?: boolean; get hasError() { return !!(this.ngControl?.status === "INVALID" && this.ngControl?.touched); diff --git a/libs/components/src/radio-button/radio-button.stories.ts b/libs/components/src/radio-button/radio-button.stories.ts index b364115ae74..eb08f04a364 100644 --- a/libs/components/src/radio-button/radio-button.stories.ts +++ b/libs/components/src/radio-button/radio-button.stories.ts @@ -1,4 +1,10 @@ -import { FormsModule, ReactiveFormsModule, FormControl, FormGroup } from "@angular/forms"; +import { + FormsModule, + ReactiveFormsModule, + FormControl, + FormGroup, + Validators, +} from "@angular/forms"; import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -165,6 +171,35 @@ export const BlockHint: Story = { }), }; +export const Required: Story = { + render: () => ({ + props: { + formObj: new FormGroup({ + radio: new FormControl(0, Validators.required), + }), + }, + template: /* HTML */ ` +
+ `, + }), +}; + export const Disabled: Story = { render: () => ({ props: { diff --git a/libs/components/src/radio-button/radio-input.component.ts b/libs/components/src/radio-button/radio-input.component.ts index 0149ffdd284..e32dc5c572d 100644 --- a/libs/components/src/radio-button/radio-input.component.ts +++ b/libs/components/src/radio-button/radio-input.component.ts @@ -1,4 +1,4 @@ -import { Component, HostBinding, input, Input, Optional, Self } from "@angular/core"; +import { booleanAttribute, Component, HostBinding, input, Optional, Self } from "@angular/core"; import { NgControl, Validators } from "@angular/forms"; import { BitFormControlAbstraction } from "../form-control"; @@ -11,6 +11,7 @@ let nextId = 0; providers: [{ provide: BitFormControlAbstraction, useExisting: RadioInputComponent }], host: { "[id]": "this.id()", + "[disabled]": "disabled", }, }) export class RadioInputComponent implements BitFormControlAbstraction { @@ -74,30 +75,17 @@ export class RadioInputComponent implements BitFormControlAbstraction { constructor(@Optional() @Self() private ngControl?: NgControl) {} - // TODO: Skipped for signal migration because: - // Accessor inputs cannot be migrated as they are too complex. - @HostBinding() - @Input() - get disabled() { - return this._disabled ?? this.ngControl?.disabled ?? false; - } - set disabled(value: any) { - this._disabled = value != null && value !== false; - } - private _disabled?: boolean; + readonly disabledInput = input(false, { transform: booleanAttribute, alias: "disabled" }); + + // TODO migrate to computed signal when Angular adds signal support to reactive forms + // https://bitwarden.atlassian.net/browse/CL-819 + get disabled() { + return this.disabledInput() || this.ngControl?.disabled || false; + } - // TODO: Skipped for signal migration because: - // Accessor inputs cannot be migrated as they are too complex. - @Input() get required() { - return ( - this._required ?? this.ngControl?.control?.hasValidator(Validators.requiredTrue) ?? false - ); + return this.ngControl?.control?.hasValidator(Validators.requiredTrue) ?? false; } - set required(value: any) { - this._required = value != null && value !== false; - } - private _required?: boolean; get hasError() { return !!(this.ngControl?.status === "INVALID" && this.ngControl?.touched);