mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 16:23:44 +00:00
[CL-707] Migrate CL codebase to signals (#15340)
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
type="radio"
|
||||
bitRadio
|
||||
[id]="inputId"
|
||||
[disabled]="groupDisabled || disabled"
|
||||
[value]="value"
|
||||
[disabled]="groupDisabled || disabled()"
|
||||
[value]="value()"
|
||||
[checked]="selected"
|
||||
(change)="onInputChange()"
|
||||
(blur)="onBlur()"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { Component, DebugElement } from "@angular/core";
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { By } from "@angular/platform-browser";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@@ -10,70 +11,62 @@ import { RadioButtonModule } from "./radio-button.module";
|
||||
import { RadioGroupComponent } from "./radio-group.component";
|
||||
|
||||
describe("RadioButton", () => {
|
||||
let mockGroupComponent: MockedButtonGroupComponent;
|
||||
let fixture: ComponentFixture<TestApp>;
|
||||
let testAppComponent: TestApp;
|
||||
let radioButton: HTMLInputElement;
|
||||
let fixture: ComponentFixture<TestComponent>;
|
||||
let radioButtonGroup: RadioGroupComponent;
|
||||
let radioButtons: DebugElement[];
|
||||
|
||||
beforeEach(async () => {
|
||||
mockGroupComponent = new MockedButtonGroupComponent();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TestApp],
|
||||
providers: [
|
||||
{ provide: RadioGroupComponent, useValue: mockGroupComponent },
|
||||
{ provide: I18nService, useValue: new I18nMockService({}) },
|
||||
],
|
||||
imports: [TestComponent],
|
||||
providers: [{ provide: I18nService, useValue: new I18nMockService({}) }],
|
||||
});
|
||||
|
||||
await TestBed.compileComponents();
|
||||
fixture = TestBed.createComponent(TestApp);
|
||||
fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.detectChanges();
|
||||
testAppComponent = fixture.debugElement.componentInstance;
|
||||
radioButton = fixture.debugElement.query(By.css("input[type=radio]")).nativeElement;
|
||||
radioButtonGroup = fixture.debugElement.query(
|
||||
By.directive(RadioGroupComponent),
|
||||
).componentInstance;
|
||||
radioButtons = fixture.debugElement.queryAll(By.css("input[type=radio]"));
|
||||
});
|
||||
|
||||
it("should emit value when clicking on radio button", () => {
|
||||
testAppComponent.value = "value";
|
||||
const spyFn = jest.spyOn(radioButtonGroup, "onInputChange");
|
||||
|
||||
radioButtons[1].triggerEventHandler("change");
|
||||
fixture.detectChanges();
|
||||
|
||||
radioButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(mockGroupComponent.onInputChange).toHaveBeenCalledWith("value");
|
||||
expect(spyFn).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it("should check radio button when selected matches value", () => {
|
||||
testAppComponent.value = "value";
|
||||
it("should check radio button only when selected matches value", () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
mockGroupComponent.selected = "value";
|
||||
expect(radioButtons[0].nativeElement.checked).toBe(true);
|
||||
expect(radioButtons[1].nativeElement.checked).toBe(false);
|
||||
|
||||
radioButtons[1].triggerEventHandler("change");
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(radioButton.checked).toBe(true);
|
||||
});
|
||||
|
||||
it("should not check radio button when selected does not match value", () => {
|
||||
testAppComponent.value = "value";
|
||||
fixture.detectChanges();
|
||||
|
||||
mockGroupComponent.selected = "nonMatchingValue";
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(radioButton.checked).toBe(false);
|
||||
expect(radioButtons[0].nativeElement.checked).toBe(false);
|
||||
expect(radioButtons[1].nativeElement.checked).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
class MockedButtonGroupComponent implements Partial<RadioGroupComponent> {
|
||||
onInputChange = jest.fn();
|
||||
selected: unknown = null;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "test-app",
|
||||
template: `<bit-radio-button [value]="value"><bit-label>Element</bit-label></bit-radio-button>`,
|
||||
imports: [RadioButtonModule],
|
||||
selector: "test-component",
|
||||
template: `
|
||||
<form [formGroup]="formObj">
|
||||
<bit-radio-group formControlName="radio">
|
||||
<bit-radio-button [value]="0"><bit-label>Element</bit-label></bit-radio-button>
|
||||
<bit-radio-button [value]="1"><bit-label>Element</bit-label></bit-radio-button>
|
||||
</bit-radio-group>
|
||||
</form>
|
||||
`,
|
||||
imports: [FormsModule, ReactiveFormsModule, RadioButtonModule],
|
||||
})
|
||||
class TestApp {
|
||||
value?: string;
|
||||
class TestComponent {
|
||||
formObj = new FormGroup({
|
||||
radio: new FormControl(0),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, HostBinding, Input } from "@angular/core";
|
||||
import { Component, HostBinding, input } from "@angular/core";
|
||||
|
||||
import { FormControlModule } from "../form-control/form-control.module";
|
||||
|
||||
@@ -11,20 +11,23 @@ let nextId = 0;
|
||||
selector: "bit-radio-button",
|
||||
templateUrl: "radio-button.component.html",
|
||||
imports: [FormControlModule, RadioInputComponent],
|
||||
host: {
|
||||
"[id]": "this.id()",
|
||||
},
|
||||
})
|
||||
export class RadioButtonComponent {
|
||||
@HostBinding("attr.id") @Input() id = `bit-radio-button-${nextId++}`;
|
||||
readonly id = input(`bit-radio-button-${nextId++}`);
|
||||
@HostBinding("class") get classList() {
|
||||
return [this.block ? "tw-block" : "tw-inline-block", "tw-mb-1", "[&_bit-hint]:tw-mt-0"];
|
||||
}
|
||||
|
||||
@Input() value: unknown;
|
||||
@Input() disabled = false;
|
||||
readonly value = input<unknown>();
|
||||
readonly disabled = input(false);
|
||||
|
||||
constructor(private groupComponent: RadioGroupComponent) {}
|
||||
|
||||
get inputId() {
|
||||
return `${this.id}-input`;
|
||||
return `${this.id()}-input`;
|
||||
}
|
||||
|
||||
get name() {
|
||||
@@ -32,7 +35,7 @@ export class RadioButtonComponent {
|
||||
}
|
||||
|
||||
get selected() {
|
||||
return this.groupComponent.selected === this.value;
|
||||
return this.groupComponent.selected === this.value();
|
||||
}
|
||||
|
||||
get groupDisabled() {
|
||||
@@ -40,11 +43,11 @@ export class RadioButtonComponent {
|
||||
}
|
||||
|
||||
get block() {
|
||||
return this.groupComponent.block;
|
||||
return this.groupComponent.block();
|
||||
}
|
||||
|
||||
protected onInputChange() {
|
||||
this.groupComponent.onInputChange(this.value);
|
||||
this.groupComponent.onInputChange(this.value());
|
||||
}
|
||||
|
||||
protected onBlur() {
|
||||
|
||||
@@ -177,15 +177,15 @@ export const Disabled: Story = {
|
||||
<bit-radio-group formControlName="radio" aria-label="Example radio group">
|
||||
<bit-label>Group of radio buttons</bit-label>
|
||||
|
||||
<bit-radio-button id="radio-first" [value]="0" [disabled]="true">
|
||||
<bit-radio-button [value]="0" [disabled]="true">
|
||||
<bit-label>First</bit-label>
|
||||
</bit-radio-button>
|
||||
|
||||
<bit-radio-button id="radio-second" [value]="1" [disabled]="true">
|
||||
<bit-radio-button [value]="1" [disabled]="true">
|
||||
<bit-label>Second</bit-label>
|
||||
</bit-radio-button>
|
||||
|
||||
<bit-radio-button id="radio-third" [value]="2" [disabled]="true">
|
||||
<bit-radio-button [value]="2" [disabled]="true">
|
||||
<bit-label>Third</bit-label>
|
||||
</bit-radio-button>
|
||||
</bit-radio-group>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { NgTemplateOutlet } from "@angular/common";
|
||||
import { Component, ContentChild, HostBinding, Input, Optional, Self } from "@angular/core";
|
||||
import { Component, ContentChild, HostBinding, Optional, Input, Self, input } from "@angular/core";
|
||||
import { ControlValueAccessor, NgControl, Validators } from "@angular/forms";
|
||||
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
@@ -14,11 +14,16 @@ let nextId = 0;
|
||||
selector: "bit-radio-group",
|
||||
templateUrl: "radio-group.component.html",
|
||||
imports: [NgTemplateOutlet, I18nPipe],
|
||||
host: {
|
||||
"[id]": "id()",
|
||||
},
|
||||
})
|
||||
export class RadioGroupComponent implements ControlValueAccessor {
|
||||
selected: unknown;
|
||||
disabled = false;
|
||||
|
||||
// TODO: Skipped for signal migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
private _name?: string;
|
||||
@Input() get name() {
|
||||
return this._name ?? this.ngControl?.name?.toString();
|
||||
@@ -27,10 +32,10 @@ export class RadioGroupComponent implements ControlValueAccessor {
|
||||
this._name = value;
|
||||
}
|
||||
|
||||
@Input() block = false;
|
||||
readonly block = input(false);
|
||||
|
||||
@HostBinding("attr.role") role = "radiogroup";
|
||||
@HostBinding("attr.id") @Input() id = `bit-radio-group-${nextId++}`;
|
||||
readonly id = input(`bit-radio-group-${nextId++}`);
|
||||
@HostBinding("class") classList = ["tw-block", "tw-mb-4"];
|
||||
|
||||
@ContentChild(BitLabel) protected label: BitLabel;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, HostBinding, Input, Optional, Self } from "@angular/core";
|
||||
import { Component, HostBinding, input, Input, Optional, Self } from "@angular/core";
|
||||
import { NgControl, Validators } from "@angular/forms";
|
||||
|
||||
import { BitFormControlAbstraction } from "../form-control";
|
||||
@@ -11,9 +11,12 @@ let nextId = 0;
|
||||
selector: "input[type=radio][bitRadio]",
|
||||
template: "",
|
||||
providers: [{ provide: BitFormControlAbstraction, useExisting: RadioInputComponent }],
|
||||
host: {
|
||||
"[id]": "this.id()",
|
||||
},
|
||||
})
|
||||
export class RadioInputComponent implements BitFormControlAbstraction {
|
||||
@HostBinding("attr.id") @Input() id = `bit-radio-input-${nextId++}`;
|
||||
readonly id = input(`bit-radio-input-${nextId++}`);
|
||||
|
||||
@HostBinding("class")
|
||||
protected inputClasses = [
|
||||
@@ -73,6 +76,8 @@ 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() {
|
||||
@@ -83,6 +88,8 @@ export class RadioInputComponent implements BitFormControlAbstraction {
|
||||
}
|
||||
private _disabled: boolean;
|
||||
|
||||
// TODO: Skipped for signal migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input()
|
||||
get required() {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user