diff --git a/README.md b/README.md index ec850886a1..343c9830e6 100644 --- a/README.md +++ b/README.md @@ -103,5 +103,5 @@ NOTE: To update the component status: | Text Field - Single | [XD](https://xd.adobe.com/view/33ffad4a-eb2c-4241-b8c5-ebfff1faf6f6-66ac/screen/842889a5-67ba-4350-91c1-55eee48f4fa2/) | | [:white_check_mark: - SB](https://ni.github.io/nimble/storybook/?path=/docs/text-field--text-field) | :white_check_mark: | :white_check_mark: | | Toggle Icon Button | [XD](https://xd.adobe.com/view/33ffad4a-eb2c-4241-b8c5-ebfff1faf6f6-66ac/screen/d022d8af-22f4-4bf2-981c-1dc0c61afece/) | | [:white_check_mark: - SB](https://ni.github.io/nimble/storybook/?path=/story/toggle-button--icon-button) | :white_check_mark: | :white_check_mark: | | Toolbar | | [Issue](https://github.com/ni/nimble/issues/411) | [:white_check_mark: - SB](https://ni.github.io/nimble/storybook/?path=/story/toolbar--toolbar) | :white_check_mark: | :white_check_mark: | -| Tooltip | [XD](https://xd.adobe.com/view/33ffad4a-eb2c-4241-b8c5-ebfff1faf6f6-66ac/screen/044414d7-1714-40f2-9679-2ce2c8202d1c/) | [Issue](https://github.com/ni/nimble/issues/309) | :o: | :o: | :o: | +| Tooltip | [XD](https://xd.adobe.com/view/33ffad4a-eb2c-4241-b8c5-ebfff1faf6f6-66ac/screen/044414d7-1714-40f2-9679-2ce2c8202d1c/) | [Issue](https://github.com/ni/nimble/issues/309) | [:arrows_counterclockwise: - SB](https://ni.github.io/nimble/storybook/?path=/docs/tooltip--tooltip) | :white_check_mark: | :o: | | Tree View | | | [:white_check_mark: - SB](https://ni.github.io/nimble/storybook/?path=/docs/tree-view--tree-view) | :white_check_mark: | :white_check_mark: | diff --git a/angular-workspace/projects/example-client-app/src/app/app.module.ts b/angular-workspace/projects/example-client-app/src/app/app.module.ts index 11585c3b1d..ac178a47f1 100644 --- a/angular-workspace/projects/example-client-app/src/app/app.module.ts +++ b/angular-workspace/projects/example-client-app/src/app/app.module.ts @@ -6,7 +6,7 @@ import { NimbleTextAreaModule, NimbleTextFieldModule, NimbleNumberFieldModule, N NimbleButtonModule, NimbleTreeViewModule, NimbleTreeItemModule, NimbleDrawerModule, NimbleThemeProviderModule, NimbleTabModule, NimbleTabPanelModule, NimbleTabsModule, NimbleTabsToolbarModule, NimbleMenuModule, NimbleMenuItemModule, NimbleCheckboxModule, NimbleToggleButtonModule, NimbleBreadcrumbModule, NimbleBreadcrumbItemModule, - NimbleIconAddModule, NimbleSwitchModule, NimbleToolbarModule, NimbleMenuButtonModule, NimbleComboboxModule, NimbleCardButtonModule } from '@ni/nimble-angular'; + NimbleIconAddModule, NimbleSwitchModule, NimbleToolbarModule, NimbleMenuButtonModule, NimbleComboboxModule, NimbleTooltipModule, NimbleCardButtonModule } from '@ni/nimble-angular'; import { AppComponent } from './app.component'; import { CustomAppComponent } from './customapp/customapp.component'; import { LoginComponent } from './login/login.component'; @@ -50,6 +50,7 @@ import { NavDrawerComponent } from './nav-drawer/nav-drawer.component'; NimbleToolbarModule, NimbleComboboxModule, NimbleMenuButtonModule, + NimbleTooltipModule, NimbleCardButtonModule, RouterModule.forRoot([ { path: '', redirectTo: '/login', pathMatch: 'full' }, diff --git a/angular-workspace/projects/example-client-app/src/app/customapp/customapp.component.html b/angular-workspace/projects/example-client-app/src/app/customapp/customapp.component.html index 21946720e1..0d9be8005a 100644 --- a/angular-workspace/projects/example-client-app/src/app/customapp/customapp.component.html +++ b/angular-workspace/projects/example-client-app/src/app/customapp/customapp.component.html @@ -162,6 +162,19 @@ Last button +
+
Tooltip
+ Default + Tooltip label + Fail + Tooltip label + Information + Tooltip label + Fail Icon + Tooltip label + Information Icon + Tooltip label +
Tree View
diff --git a/angular-workspace/projects/ni/nimble-angular/src/directives/tooltip/nimble-tooltip.directive.ts b/angular-workspace/projects/ni/nimble-angular/src/directives/tooltip/nimble-tooltip.directive.ts new file mode 100644 index 0000000000..06e65189a7 --- /dev/null +++ b/angular-workspace/projects/ni/nimble-angular/src/directives/tooltip/nimble-tooltip.directive.ts @@ -0,0 +1,33 @@ +import { Directive, ElementRef, Input, Renderer2 } from '@angular/core'; +import type { Tooltip } from '@ni/nimble-components/dist/esm/tooltip'; +import { TooltipStatus } from '@ni/nimble-components/dist/esm/tooltip/types'; +import { NumberValueOrAttribute, toNumberProperty } from '../utilities/template-value-helpers'; + +export type { Tooltip }; +export { TooltipStatus }; + +/** + * Directive to provide Angular integration for the tooltip. + */ +@Directive({ + selector: 'nimble-tooltip' +}) +export class NimbleTooltipDirective { + public get anchor(): string { + return this.elementRef.nativeElement.anchor; + } + + @Input() public set anchor(value: string) { + this.renderer.setProperty(this.elementRef.nativeElement, 'anchor', value); + } + + public get delay(): number { + return this.elementRef.nativeElement.delay; + } + + @Input() public set delay(value: NumberValueOrAttribute) { + this.renderer.setProperty(this.elementRef.nativeElement, 'delay', toNumberProperty(value)); + } + + public constructor(private readonly renderer: Renderer2, private readonly elementRef: ElementRef) {} +} \ No newline at end of file diff --git a/angular-workspace/projects/ni/nimble-angular/src/directives/tooltip/nimble-tooltip.module.ts b/angular-workspace/projects/ni/nimble-angular/src/directives/tooltip/nimble-tooltip.module.ts new file mode 100644 index 0000000000..40763db322 --- /dev/null +++ b/angular-workspace/projects/ni/nimble-angular/src/directives/tooltip/nimble-tooltip.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { NimbleTooltipDirective } from './nimble-tooltip.directive'; + +import '@ni/nimble-components/dist/esm/tooltip'; + +@NgModule({ + declarations: [NimbleTooltipDirective], + imports: [CommonModule], + exports: [NimbleTooltipDirective] +}) +export class NimbleTooltipModule { } \ No newline at end of file diff --git a/angular-workspace/projects/ni/nimble-angular/src/directives/tooltip/tests/nimble-tooltip.directive.spec.ts b/angular-workspace/projects/ni/nimble-angular/src/directives/tooltip/tests/nimble-tooltip.directive.spec.ts new file mode 100644 index 0000000000..f51f0a6e11 --- /dev/null +++ b/angular-workspace/projects/ni/nimble-angular/src/directives/tooltip/tests/nimble-tooltip.directive.spec.ts @@ -0,0 +1,210 @@ +import { Component, ElementRef, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import type { NumberValueOrAttribute } from 'dist/ni/nimble-angular/directives/utilities/template-value-helpers'; +import { Tooltip, NimbleTooltipDirective, TooltipStatus } from '../nimble-tooltip.directive'; +import { NimbleTooltipModule } from '../nimble-tooltip.module'; + +describe('Nimble tooltip', () => { + describe('module', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [NimbleTooltipModule] + }); + }); + + it('defines custom element', () => { + expect(customElements.get('nimble-tooltip')).not.toBeUndefined(); + }); + }); + + describe('TooltipStatus', () => { + it('can use TooltipStatus values', () => { + // Ensure TooltipStatus is exported correctly so that it can be used + // as more than a type. + expect(TooltipStatus.information).toEqual(TooltipStatus.information); + }); + }); + + describe('with no values in template', () => { + @Component({ + template: ` + + ` + }) + class TestHostComponent { + @ViewChild('tooltip', { read: NimbleTooltipDirective }) public directive: NimbleTooltipDirective; + @ViewChild('tooltip', { read: ElementRef }) public elementRef: ElementRef; + } + + let fixture: ComponentFixture; + let directive: NimbleTooltipDirective; + let nativeElement: Tooltip; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [TestHostComponent], + imports: [NimbleTooltipModule] + }); + fixture = TestBed.createComponent(TestHostComponent); + fixture.detectChanges(); + directive = fixture.componentInstance.directive; + nativeElement = fixture.componentInstance.elementRef.nativeElement; + }); + + it('has expected defaults for anchor', () => { + expect(directive.anchor).toBe(''); + expect(nativeElement.anchor).toBe(''); + }); + + it('has expected defaults for delay', () => { + expect(directive.delay).toBe(300); + expect(nativeElement.delay).toBe(300); + }); + }); + + describe('with template string values', () => { + @Component({ + template: ` + + ` + }) + class TestHostComponent { + @ViewChild('tooltip', { read: NimbleTooltipDirective }) public directive: NimbleTooltipDirective; + @ViewChild('tooltip', { read: ElementRef }) public elementRef: ElementRef; + } + + let fixture: ComponentFixture; + let directive: NimbleTooltipDirective; + let nativeElement: Tooltip; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [TestHostComponent], + imports: [NimbleTooltipModule] + }); + fixture = TestBed.createComponent(TestHostComponent); + fixture.detectChanges(); + directive = fixture.componentInstance.directive; + nativeElement = fixture.componentInstance.elementRef.nativeElement; + }); + + it('will use template string values for anchor', () => { + expect(directive.anchor).toBe('anchor'); + expect(nativeElement.anchor).toBe('anchor'); + }); + + it('will use template string values for delay', () => { + expect(directive.delay).toBe(300); + expect(nativeElement.delay).toBe(300); + }); + }); + + describe('with property bound values', () => { + @Component({ + template: ` + + + ` + }) + class TestHostComponent { + @ViewChild('tooltip', { read: NimbleTooltipDirective }) public directive: NimbleTooltipDirective; + @ViewChild('tooltip', { read: ElementRef }) public elementRef: ElementRef; + public anchor = 'anchor'; + public delay = 300; + } + + let fixture: ComponentFixture; + let directive: NimbleTooltipDirective; + let nativeElement: Tooltip; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [TestHostComponent], + imports: [NimbleTooltipModule] + }); + fixture = TestBed.createComponent(TestHostComponent); + fixture.detectChanges(); + directive = fixture.componentInstance.directive; + nativeElement = fixture.componentInstance.elementRef.nativeElement; + }); + + it('can be configured with property binding for anchor', () => { + expect(directive.anchor).toBe('anchor'); + expect(nativeElement.anchor).toBe('anchor'); + + fixture.componentInstance.anchor = 'anchor2'; + fixture.detectChanges(); + + expect(directive.anchor).toBe('anchor2'); + expect(nativeElement.anchor).toBe('anchor2'); + }); + it('can be configured with property binding for delay', () => { + expect(directive.delay).toBe(300); + expect(nativeElement.delay).toBe(300); + + fixture.componentInstance.delay = 400; + fixture.detectChanges(); + + expect(directive.delay).toBe(400); + expect(nativeElement.delay).toBe(400); + }); + }); + + describe('with attribute bound values', () => { + @Component({ + template: ` + + + ` + }) + class TestHostComponent { + @ViewChild('tooltip', { read: NimbleTooltipDirective }) public directive: NimbleTooltipDirective; + @ViewChild('tooltip', { read: ElementRef }) public elementRef: ElementRef; + public anchor = 'anchor'; + public delay: NumberValueOrAttribute = 300; + } + + let fixture: ComponentFixture; + let directive: NimbleTooltipDirective; + let nativeElement: Tooltip; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [TestHostComponent], + imports: [NimbleTooltipModule] + }); + fixture = TestBed.createComponent(TestHostComponent); + fixture.detectChanges(); + directive = fixture.componentInstance.directive; + nativeElement = fixture.componentInstance.elementRef.nativeElement; + }); + + it('can be configured with attribute binding for anchor', () => { + expect(directive.anchor).toBe('anchor'); + expect(nativeElement.anchor).toBe('anchor'); + + fixture.componentInstance.anchor = 'anchor2'; + fixture.detectChanges(); + + expect(directive.anchor).toBe('anchor2'); + expect(nativeElement.anchor).toBe('anchor2'); + }); + // Test is disabled because of [FAST bug](https://github.com/microsoft/fast/issues/6257) + xit('can be configured with attribute binding for delay', () => { + expect(directive.delay).toBe(300); + expect(nativeElement.delay).toBe(300); + + fixture.componentInstance.delay = 400; + fixture.detectChanges(); + + expect(directive.delay).toBe(400); + expect(nativeElement.delay).toBe(400); + }); + }); +}); diff --git a/angular-workspace/projects/ni/nimble-angular/src/public-api.ts b/angular-workspace/projects/ni/nimble-angular/src/public-api.ts index 32740b2dec..30bdb8dc71 100644 --- a/angular-workspace/projects/ni/nimble-angular/src/public-api.ts +++ b/angular-workspace/projects/ni/nimble-angular/src/public-api.ts @@ -64,6 +64,8 @@ export * from './directives/tree-item/nimble-tree-item.directive'; export * from './directives/tree-item/nimble-tree-item.module'; export * from './directives/tree-view/nimble-tree-view.directive'; export * from './directives/tree-view/nimble-tree-view.module'; +export * from './directives/tooltip/nimble-tooltip.directive'; +export * from './directives/tooltip/nimble-tooltip.module'; export * from './testing/async-helpers'; // Export enums that are used by multiple components here to avoid exporting them multiple times. diff --git a/change/@ni-nimble-angular-95820796-03e5-42c0-aec1-3dbd10c45311.json b/change/@ni-nimble-angular-95820796-03e5-42c0-aec1-3dbd10c45311.json new file mode 100644 index 0000000000..9e7adb57e4 --- /dev/null +++ b/change/@ni-nimble-angular-95820796-03e5-42c0-aec1-3dbd10c45311.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Tooltip Angular Integration (#309)", + "packageName": "@ni/nimble-angular", + "email": "103057880+aidendk@users.noreply.github.com", + "dependentChangeType": "patch" +}