Skip to content

Commit

Permalink
Angular and Blazor support for appearance-variant on menu and toggle …
Browse files Browse the repository at this point in the history
…buttons (#1971)

# Pull Request

## 🤨 Rationale

Completes: #1906

## 👩‍💻 Implementation

Exposed appearance-variant from Angular and Blazor APIs for menu and
toggle button.

## 🧪 Testing

Added standard test cases

## ✅ Checklist

- [x] I have updated the project documentation to reflect my changes or
determined no changes are needed.

---------

Co-authored-by: Milan Raj <rajsite@users.noreply.github.com>
  • Loading branch information
m-akinc and rajsite authored Mar 29, 2024
1 parent f71ba85 commit 82d484e
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Directive, ElementRef, Input, Renderer2 } from '@angular/core';
import { type MenuButton, menuButtonTag } from '@ni/nimble-components/dist/esm/menu-button';
import type { ButtonAppearance, MenuButtonToggleEventDetail } from '@ni/nimble-components/dist/esm/menu-button/types';
import type { ButtonAppearance, ButtonAppearanceVariant, MenuButtonToggleEventDetail } from '@ni/nimble-components/dist/esm/menu-button/types';
import { BooleanValueOrAttribute, toBooleanProperty } from '@ni/nimble-angular/internal-utilities';

export type { MenuButton };
Expand All @@ -22,6 +22,14 @@ export class NimbleMenuButtonDirective {
this.renderer.setProperty(this.elementRef.nativeElement, 'appearance', value);
}

public get appearanceVariant(): ButtonAppearanceVariant {
return this.elementRef.nativeElement.appearanceVariant;
}

@Input('appearance-variant') public set appearanceVariant(value: ButtonAppearanceVariant) {
this.renderer.setProperty(this.elementRef.nativeElement, 'appearanceVariant', value);
}

public get disabled(): boolean {
return this.elementRef.nativeElement.disabled;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, ElementRef, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import type { BooleanValueOrAttribute } from '@ni/nimble-angular/internal-utilities';
import { ButtonAppearance } from '../../../public-api';
import { ButtonAppearance, ButtonAppearanceVariant } from '../../../public-api';
import { NimbleMenuButtonDirective, MenuButton } from '../nimble-menu-button.directive';
import { NimbleMenuButtonModule } from '../nimble-menu-button.module';

Expand Down Expand Up @@ -54,6 +54,11 @@ describe('Nimble menu button', () => {
expect(nativeElement.appearance).toBe(ButtonAppearance.outline);
});

it('has expected defaults for appearanceVariant', () => {
expect(directive.appearanceVariant).toBe(ButtonAppearanceVariant.default);
expect(nativeElement.appearanceVariant).toBe(ButtonAppearanceVariant.default);
});

it('has expected defaults for contentHidden', () => {
expect(directive.contentHidden).toBeFalse();
expect(nativeElement.contentHidden).toBeFalse();
Expand All @@ -71,6 +76,7 @@ describe('Nimble menu button', () => {
<nimble-menu-button #menuButton
disabled
appearance="${ButtonAppearance.ghost}"
appearance-variant="${ButtonAppearanceVariant.primary}"
content-hidden
open>
</nimble-menu-button>`
Expand Down Expand Up @@ -105,6 +111,11 @@ describe('Nimble menu button', () => {
expect(nativeElement.appearance).toBe(ButtonAppearance.ghost);
});

it('will use template string values for appearanceVariant', () => {
expect(directive.appearanceVariant).toBe(ButtonAppearanceVariant.primary);
expect(nativeElement.appearanceVariant).toBe(ButtonAppearanceVariant.primary);
});

it('will use template string values for contentHidden', () => {
expect(directive.contentHidden).toBeTrue();
expect(nativeElement.contentHidden).toBeTrue();
Expand All @@ -122,6 +133,7 @@ describe('Nimble menu button', () => {
<nimble-menu-button #menuButton
[disabled]="disabled"
[appearance]="appearance"
[appearance-variant]="appearanceVariant"
[content-hidden]="contentHidden"
[open]="open">
</nimble-menu-button>
Expand All @@ -132,6 +144,7 @@ describe('Nimble menu button', () => {
@ViewChild('menuButton', { read: ElementRef }) public elementRef: ElementRef<MenuButton>;
public disabled = false;
public appearance: ButtonAppearance = ButtonAppearance.outline;
public appearanceVariant: ButtonAppearanceVariant;
public contentHidden = false;
public open = false;
}
Expand Down Expand Up @@ -173,6 +186,17 @@ describe('Nimble menu button', () => {
expect(nativeElement.appearance).toBe(ButtonAppearance.ghost);
});

it('can be configured with property binding for appearanceVariant', () => {
expect(directive.appearanceVariant).toBe(ButtonAppearanceVariant.default);
expect(nativeElement.appearanceVariant).toBe(ButtonAppearanceVariant.default);

fixture.componentInstance.appearanceVariant = ButtonAppearanceVariant.primary;
fixture.detectChanges();

expect(directive.appearanceVariant).toBe(ButtonAppearanceVariant.primary);
expect(nativeElement.appearanceVariant).toBe(ButtonAppearanceVariant.primary);
});

it('can be configured with property binding for contentHidden', () => {
expect(directive.contentHidden).toBeFalse();
expect(nativeElement.contentHidden).toBeFalse();
Expand Down Expand Up @@ -202,6 +226,7 @@ describe('Nimble menu button', () => {
<nimble-menu-button #menuButton
[attr.disabled]="disabled"
[attr.appearance]="appearance"
[attr.appearance-variant]="appearanceVariant"
[attr.content-hidden]="contentHidden"
[attr.open]="open">
</nimble-menu-button>
Expand All @@ -212,6 +237,7 @@ describe('Nimble menu button', () => {
@ViewChild('menuButton', { read: ElementRef }) public elementRef: ElementRef<MenuButton>;
public disabled: BooleanValueOrAttribute = null;
public appearance: ButtonAppearance = ButtonAppearance.outline;
public appearanceVariant: ButtonAppearanceVariant;
public contentHidden: BooleanValueOrAttribute = null;
public open: BooleanValueOrAttribute = null;
}
Expand Down Expand Up @@ -253,6 +279,17 @@ describe('Nimble menu button', () => {
expect(nativeElement.appearance).toBe(ButtonAppearance.ghost);
});

it('can be configured with attribute binding for appearanceVariant', () => {
expect(directive.appearanceVariant).toBe(ButtonAppearanceVariant.default);
expect(nativeElement.appearanceVariant).toBe(ButtonAppearanceVariant.default);

fixture.componentInstance.appearanceVariant = ButtonAppearanceVariant.primary;
fixture.detectChanges();

expect(directive.appearanceVariant).toBe(ButtonAppearanceVariant.primary);
expect(nativeElement.appearanceVariant).toBe(ButtonAppearanceVariant.primary);
});

it('can be configured with attribute binding for contentHidden', () => {
expect(directive.contentHidden).toBeFalse();
expect(nativeElement.contentHidden).toBeFalse();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Directive, ElementRef, Input, Renderer2 } from '@angular/core';
import { type ToggleButton, toggleButtonTag } from '@ni/nimble-components/dist/esm/toggle-button';
import type { ButtonAppearance } from '@ni/nimble-components/dist/esm/toggle-button/types';
import type { ButtonAppearance, ButtonAppearanceVariant } from '@ni/nimble-components/dist/esm/toggle-button/types';
import { BooleanValueOrAttribute, toBooleanProperty } from '@ni/nimble-angular/internal-utilities';

export type { ToggleButton };
Expand All @@ -21,6 +21,14 @@ export class NimbleToggleButtonDirective {
this.renderer.setProperty(this.elementRef.nativeElement, 'appearance', value);
}

public get appearanceVariant(): ButtonAppearanceVariant {
return this.elementRef.nativeElement.appearanceVariant;
}

@Input('appearance-variant') public set appearanceVariant(value: ButtonAppearanceVariant) {
this.renderer.setProperty(this.elementRef.nativeElement, 'appearanceVariant', value);
}

public get disabled(): boolean {
return this.elementRef.nativeElement.disabled;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, ElementRef, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import type { BooleanValueOrAttribute } from '@ni/nimble-angular/internal-utilities';
import { ButtonAppearance } from '../../../public-api';
import { ButtonAppearance, ButtonAppearanceVariant } from '../../../public-api';
import { NimbleToggleButtonDirective, ToggleButton } from '../nimble-toggle-button.directive';
import { NimbleToggleButtonModule } from '../nimble-toggle-button.module';

Expand Down Expand Up @@ -54,6 +54,11 @@ describe('Nimble toggle button', () => {
expect(nativeElement.appearance).toBe(ButtonAppearance.outline);
});

it('has expected defaults for appearanceVariant', () => {
expect(directive.appearanceVariant).toBe(ButtonAppearanceVariant.default);
expect(nativeElement.appearanceVariant).toBe(ButtonAppearanceVariant.default);
});

it('has expected defaults for contentHidden', () => {
expect(directive.contentHidden).toBeFalse();
expect(nativeElement.contentHidden).toBeFalse();
Expand All @@ -71,6 +76,7 @@ describe('Nimble toggle button', () => {
<nimble-toggle-button #toggleButton
disabled
appearance="${ButtonAppearance.ghost}"
appearance-variant="${ButtonAppearanceVariant.primary}"
content-hidden
checked>
</nimble-toggle-button>`
Expand Down Expand Up @@ -105,6 +111,11 @@ describe('Nimble toggle button', () => {
expect(nativeElement.appearance).toBe(ButtonAppearance.ghost);
});

it('will use template string values for appearanceVariant', () => {
expect(directive.appearanceVariant).toBe(ButtonAppearanceVariant.primary);
expect(nativeElement.appearanceVariant).toBe(ButtonAppearanceVariant.primary);
});

it('will use template string values for contentHidden', () => {
expect(directive.contentHidden).toBeTrue();
expect(nativeElement.contentHidden).toBeTrue();
Expand All @@ -122,6 +133,7 @@ describe('Nimble toggle button', () => {
<nimble-toggle-button #toggleButton
[disabled]="disabled"
[appearance]="appearance"
[appearance-variant]="appearanceVariant"
[content-hidden]="contentHidden"
[checked]="checked">
</nimble-toggle-button>
Expand All @@ -132,6 +144,7 @@ describe('Nimble toggle button', () => {
@ViewChild('toggleButton', { read: ElementRef }) public elementRef: ElementRef<ToggleButton>;
public disabled = false;
public appearance: ButtonAppearance = ButtonAppearance.outline;
public appearanceVariant: ButtonAppearanceVariant;
public contentHidden = false;
public checked = false;
}
Expand Down Expand Up @@ -173,6 +186,17 @@ describe('Nimble toggle button', () => {
expect(nativeElement.appearance).toBe(ButtonAppearance.ghost);
});

it('can be configured with property binding for appearanceVariant', () => {
expect(directive.appearanceVariant).toBe(ButtonAppearanceVariant.default);
expect(nativeElement.appearanceVariant).toBe(ButtonAppearanceVariant.default);

fixture.componentInstance.appearanceVariant = ButtonAppearanceVariant.primary;
fixture.detectChanges();

expect(directive.appearanceVariant).toBe(ButtonAppearanceVariant.primary);
expect(nativeElement.appearanceVariant).toBe(ButtonAppearanceVariant.primary);
});

it('can be configured with property binding for contentHidden', () => {
expect(directive.contentHidden).toBeFalse();
expect(nativeElement.contentHidden).toBeFalse();
Expand Down Expand Up @@ -202,6 +226,7 @@ describe('Nimble toggle button', () => {
<nimble-toggle-button #toggleButton
[attr.disabled]="disabled"
[attr.appearance]="appearance"
[attr.appearance-variant]="appearanceVariant"
[attr.content-hidden]="contentHidden"
[attr.checked]="checked">
</nimble-toggle-button>
Expand All @@ -212,6 +237,7 @@ describe('Nimble toggle button', () => {
@ViewChild('toggleButton', { read: ElementRef }) public elementRef: ElementRef<ToggleButton>;
public disabled: BooleanValueOrAttribute = null;
public appearance: ButtonAppearance = ButtonAppearance.outline;
public appearanceVariant: ButtonAppearanceVariant;
public contentHidden: BooleanValueOrAttribute = null;
public checked: BooleanValueOrAttribute = null;
}
Expand Down Expand Up @@ -253,6 +279,17 @@ describe('Nimble toggle button', () => {
expect(nativeElement.appearance).toBe(ButtonAppearance.ghost);
});

it('can be configured with attribute binding for appearanceVariant', () => {
expect(directive.appearanceVariant).toBe(ButtonAppearanceVariant.default);
expect(nativeElement.appearanceVariant).toBe(ButtonAppearanceVariant.default);

fixture.componentInstance.appearanceVariant = ButtonAppearanceVariant.primary;
fixture.detectChanges();

expect(directive.appearanceVariant).toBe(ButtonAppearanceVariant.primary);
expect(nativeElement.appearanceVariant).toBe(ButtonAppearanceVariant.primary);
});

it('can be configured with attribute binding for contentHidden', () => {
expect(directive.contentHidden).toBeFalse();
expect(nativeElement.contentHidden).toBeFalse();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Angular support for appearance-variant on toggle and menu buttons",
"packageName": "@ni/nimble-angular",
"email": "7282195+m-akinc@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Blazor support for appearance-variant on menu and toggle buttons",
"packageName": "@ni/nimble-blazor",
"email": "7282195+m-akinc@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@onnimblemenubuttontoggle="(__value) => HandleToggle(__value)"
@onnimblemenubuttonbeforetoggle="(__value) => HandleBeforeToggle(__value)"
appearance="@Appearance.ToAttributeValue()"
appearance-variant="@AppearanceVariant.ToAttributeValue()"
position="@Position.ToAttributeValue()"
disabled="@Disabled"
autofocus="@AutoFocus"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ public partial class NimbleMenuButton : ComponentBase
[Parameter]
public ButtonAppearance? Appearance { get; set; }

/// <summary>
/// Gets or sets the appearance variant of the menu button.
/// </summary>
[Parameter]
public ButtonAppearanceVariant? AppearanceVariant { get; set; }

/// <summary>
/// Gets or sets whether or not the menu is open.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
current-checked="@BindConverter.FormatValue(CurrentValue)"
@onnimblecheckedchange="(__value) => CurrentValue = __value.Checked"
appearance="@Appearance.ToAttributeValue()"
appearance-variant="@AppearanceVariant.ToAttributeValue()"
disabled="@Disabled"
autofocus="@AutoFocus"
content-hidden="@ContentHidden"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ public partial class NimbleToggleButton : NimbleInputBase<bool>
[Parameter]
public ButtonAppearance? Appearance { get; set; }

[Parameter]
public ButtonAppearanceVariant? AppearanceVariant { get; set; }

[Parameter]
public bool? Disabled { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ public void ButtonAppearance_AttributeIsSet(ButtonAppearance value, string expec
Assert.Contains(expectedAttribute, button.Markup);
}

[Theory]
[InlineData(ButtonAppearanceVariant.Default, "<nimble-menu-button((?!appearance-variant).)*>")]
[InlineData(ButtonAppearanceVariant.Primary, "appearance-variant=\"primary\"")]
[InlineData(ButtonAppearanceVariant.Accent, "appearance-variant=\"accent\"")]
public void ButtonAppearanceVariant_AttributeIsSet(ButtonAppearanceVariant value, string expectedAttribute)
{
var button = RenderNimbleMenuButton(value);

Assert.Matches(expectedAttribute, button.Markup);
}

[Theory]
[InlineData(MenuButtonPosition.Above, "above")]
[InlineData(MenuButtonPosition.Below, "below")]
Expand All @@ -58,6 +69,13 @@ private IRenderedComponent<NimbleMenuButton> RenderNimbleMenuButton(ButtonAppear
return context.RenderComponent<NimbleMenuButton>(p => p.Add(x => x.Appearance, appearance));
}

private IRenderedComponent<NimbleMenuButton> RenderNimbleMenuButton(ButtonAppearanceVariant appearanceVariant)
{
var context = new TestContext();
context.JSInterop.Mode = JSRuntimeMode.Loose;
return context.RenderComponent<NimbleMenuButton>(p => p.Add(x => x.AppearanceVariant, appearanceVariant));
}

private IRenderedComponent<NimbleMenuButton> RenderNimbleMenuButton(MenuButtonPosition position)
{
var context = new TestContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,28 @@ public void ButtonAppearance_AttributeIsSet(ButtonAppearance value, string expec
Assert.Contains(expectedAttribute, button.Markup);
}

[Theory]
[InlineData(ButtonAppearanceVariant.Default, "<nimble-toggle-button((?!appearance-variant).)*>")]
[InlineData(ButtonAppearanceVariant.Primary, "appearance-variant=\"primary\"")]
[InlineData(ButtonAppearanceVariant.Accent, "appearance-variant=\"accent\"")]
public void ButtonAppearanceVariant_AttributeIsSet(ButtonAppearanceVariant value, string expectedAttribute)
{
var button = RenderNimbleToggleButton(value);

Assert.Matches(expectedAttribute, button.Markup);
}

private IRenderedComponent<NimbleToggleButton> RenderNimbleToggleButton(ButtonAppearance appearance)
{
var context = new TestContext();
context.JSInterop.Mode = JSRuntimeMode.Loose;
return context.RenderComponent<NimbleToggleButton>(p => p.Add(x => x.Appearance, appearance));
}

private IRenderedComponent<NimbleToggleButton> RenderNimbleToggleButton(ButtonAppearanceVariant appearanceVariant)
{
var context = new TestContext();
context.JSInterop.Mode = JSRuntimeMode.Loose;
return context.RenderComponent<NimbleToggleButton>(p => p.Add(x => x.AppearanceVariant, appearanceVariant));
}
}

0 comments on commit 82d484e

Please sign in to comment.