Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support error state on radio group in Angular and Blazor #2433

Merged
merged 5 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Expose error state on radio-group",
"packageName": "@ni/nimble-angular",
"email": "20542556+mollykreis@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Expose error state on radio-group",
"packageName": "@ni/nimble-blazor",
"email": "20542556+mollykreis@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
<div class="sub-container">
<div class="container-label">Radio Buttons</div>
<nimble-radio-group>
<span slot="label">Fruit</span>
<nimble-radio name="fruit" value="apple" [(ngModel)]="selectedRadio">Apple</nimble-radio>
<nimble-radio name="fruit" value="banana" [(ngModel)]="selectedRadio">Banana</nimble-radio>
<nimble-radio name="fruit" value="mango" [(ngModel)]="selectedRadio">Mango</nimble-radio>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,21 @@ export class NimbleRadioGroupDirective {
this.renderer.setProperty(this.elementRef.nativeElement, 'orientation', value);
}

public get errorText(): string | undefined {
return this.elementRef.nativeElement.errorText;
}

@Input('error-text') public set errorText(value: string | undefined) {
this.renderer.setProperty(this.elementRef.nativeElement, 'errorText', value);
}

public get errorVisible(): boolean {
return this.elementRef.nativeElement.errorVisible;
}

@Input('error-visible') public set errorVisible(value: BooleanValueOrAttribute) {
this.renderer.setProperty(this.elementRef.nativeElement, 'errorVisible', toBooleanProperty(value));
}

public constructor(private readonly renderer: Renderer2, private readonly elementRef: ElementRef<RadioGroup>) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,28 @@ describe('Nimble radio group', () => {
directive.orientation = Orientation.vertical;
expect(nativeElement.orientation).toBe(Orientation.vertical);
});

it('has expected defaults for errorText', () => {
expect(directive.errorText).toBeUndefined();
expect(nativeElement.errorText).toBeUndefined();
});

it('can use the directive to set errorText', () => {
directive.errorText = 'new value';
expect(nativeElement.errorText).toBe('new value');
});
});

describe('with template string values', () => {
@Component({
template: '<nimble-radio-group #radioGroup disabled name="foo" orientation="vertical"></nimble-radio-group>'
template: `
<nimble-radio-group #radioGroup
disabled
name="foo"
orientation="vertical"
error-text="error text"
error-visible
></nimble-radio-group>`
})
class TestHostComponent {
@ViewChild('radioGroup', { read: NimbleRadioGroupDirective }) public directive: NimbleRadioGroupDirective;
Expand Down Expand Up @@ -110,18 +127,37 @@ describe('Nimble radio group', () => {
expect(directive.orientation).toBe(Orientation.vertical);
expect(nativeElement.orientation).toBe(Orientation.vertical);
});

it('will use template string values for errorText', () => {
expect(directive.errorText).toBe('error text');
expect(nativeElement.errorText).toBe('error text');
});

it('will use template string values for errorVisible', () => {
expect(directive.errorVisible).toBeTrue();
expect(nativeElement.errorVisible).toBeTrue();
});
});

describe('with property bound values', () => {
@Component({
template: '<nimble-radio-group #radioGroup [disabled]="disabled" [name]="name" [orientation]="orientation"></nimble-radio-group>'
template: `
<nimble-radio-group #radioGroup
[disabled]="disabled"
[name]="name"
[orientation]="orientation"
[error-text]="errorText"
[error-visible]="errorVisible"
></nimble-radio-group>`
})
class TestHostComponent {
@ViewChild('radioGroup', { read: NimbleRadioGroupDirective }) public directive: NimbleRadioGroupDirective;
@ViewChild('radioGroup', { read: ElementRef }) public elementRef: ElementRef<RadioGroup>;
public disabled = false;
public name = 'foo';
public orientation: Orientation = Orientation.vertical;
public errorText = 'initial value';
public errorVisible = false;
}

let fixture: ComponentFixture<TestHostComponent>;
Expand Down Expand Up @@ -171,18 +207,49 @@ describe('Nimble radio group', () => {
expect(directive.orientation).toBe(Orientation.horizontal);
expect(nativeElement.orientation).toBe(Orientation.horizontal);
});

it('can be configured with property binding for errorText', () => {
expect(directive.errorText).toBe('initial value');
expect(nativeElement.errorText).toBe('initial value');

fixture.componentInstance.errorText = 'new value';
fixture.detectChanges();

expect(directive.errorText).toBe('new value');
expect(nativeElement.errorText).toBe('new value');
});

it('can be configured with property binding for errorVisible', () => {
expect(directive.errorVisible).toBeFalse();
expect(nativeElement.errorVisible).toBeFalse();

fixture.componentInstance.errorVisible = true;
fixture.detectChanges();

expect(directive.errorVisible).toBeTrue();
expect(nativeElement.errorVisible).toBeTrue();
});
});

describe('with attribute bound values', () => {
@Component({
template: '<nimble-radio-group #radioGroup [attr.disabled]="disabled" [attr.name]="name" [attr.orientation]="orientation"></nimble-radio-group>'
template: `
<nimble-radio-group #radioGroup
[attr.disabled]="disabled"
[attr.name]="name"
[attr.orientation]="orientation"
[attr.error-text]="errorText"
[attr.error-visible]="errorVisible"
></nimble-radio-group>`
})
class TestHostComponent {
@ViewChild('radioGroup', { read: NimbleRadioGroupDirective }) public directive: NimbleRadioGroupDirective;
@ViewChild('radioGroup', { read: ElementRef }) public elementRef: ElementRef<RadioGroup>;
public disabled: BooleanValueOrAttribute = null;
public name = 'foo';
public orientation: Orientation = Orientation.vertical;
public errorText = 'initial value';
public errorVisible: BooleanValueOrAttribute = null;
}

let fixture: ComponentFixture<TestHostComponent>;
Expand Down Expand Up @@ -232,5 +299,27 @@ describe('Nimble radio group', () => {
expect(directive.orientation).toBe(Orientation.horizontal);
expect(nativeElement.orientation).toBe(Orientation.horizontal);
});

it('can be configured with attribute binding for errorText', () => {
expect(directive.errorText).toBe('initial value');
expect(nativeElement.errorText).toBe('initial value');

fixture.componentInstance.errorText = 'new value';
fixture.detectChanges();

expect(directive.errorText).toBe('new value');
expect(nativeElement.errorText).toBe('new value');
});

it('can be configured with attribute binding for errorVisible', () => {
expect(directive.errorVisible).toBeFalse();
expect(nativeElement.errorVisible).toBeFalse();

fixture.componentInstance.errorVisible = '';
fixture.detectChanges();

expect(directive.errorVisible).toBeTrue();
expect(nativeElement.errorVisible).toBeTrue();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
<div class="sub-container">
<div class="container-label">Radio Buttons</div>
<NimbleRadioGroup Name="options" @bind-Value="@SelectedRadio">
<span slot="label">Options</span>
<NimbleRadio Value="1">Option 1</NimbleRadio>
<NimbleRadio Value="2">Option 2</NimbleRadio>
<NimbleRadio Value="3">Option 3</NimbleRadio>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<nimble-radio-group disabled=@Disabled
name="@Name"
orientation="@Orientation.ToAttributeValue()"
error-visible="@ErrorVisible"
error-text="@ErrorText"
value="@Value"
@onchange="@(EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValue = __value, CurrentValue))"
@attributes="AdditionalAttributes">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ public partial class NimbleRadioGroup : NimbleInputBase<string>
[Parameter]
public Orientation? Orientation { get; set; }

/// <summary>
/// Gets or sets whether the error state is displayed
/// </summary>
[Parameter]
public bool? ErrorVisible { get; set; }

/// <summary>
/// Gets or sets an error message describing the error state
/// </summary>
[Parameter]
public string? ErrorText { get; set; }

/// <summary>
/// Gets or sets the child content to be rendered inside the <see cref="NimbleRadioGroup"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ public void NimbleRadioGroupName_AttributeIsSet()
Assert.Contains("name", radioGroup.Markup);
}

[Fact]
public void NimbleRadioGroupErrorText_AttributeIsSet()
{
var radioGroup = RenderNimbleRadioGroupWithPropertySet(x => x.ErrorText, "bad value");

Assert.Contains("error-text=\"bad value\"", radioGroup.Markup);
}

[Fact]
public void NimbleRadioGroupErrorVisible_AttributeIsSet()
{
var radioGroup = RenderNimbleRadioGroupWithPropertySet(x => x.ErrorVisible, true);

Assert.Contains("error-visible", radioGroup.Markup);
}

private IRenderedComponent<NimbleRadioGroup> RenderNimbleRadioGroupWithPropertySet<TProperty>(Expression<Func<NimbleRadioGroup, TProperty>> propertyGetter, TProperty propertyValue)
{
var context = new TestContext();
Expand Down