diff --git a/angular-workspace/package.json b/angular-workspace/package.json
index 4828994aac..8850fcb683 100644
--- a/angular-workspace/package.json
+++ b/angular-workspace/package.json
@@ -60,6 +60,6 @@
"ng-packagr": "^15.2.2",
"playwright": "1.40.0",
"rollup": "^4.12.0",
- "typescript": "~4.8.2"
+ "typescript": "~4.9.5"
}
}
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 5c840835bb..2bd837f9c1 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
@@ -151,16 +151,19 @@
Select
+ Select an option
Option 1
Option 2
Option 3
+ Select an option
Option 1
Option 2
Option 3
+ Select an option
Option 1
Option 2
Option 3
diff --git a/angular-workspace/projects/example-client-app/src/app/customapp/customapp.component.ts b/angular-workspace/projects/example-client-app/src/app/customapp/customapp.component.ts
index ce06158d7d..5704fd8fbc 100644
--- a/angular-workspace/projects/example-client-app/src/app/customapp/customapp.component.ts
+++ b/angular-workspace/projects/example-client-app/src/app/customapp/customapp.component.ts
@@ -110,14 +110,6 @@ export class CustomAppComponent implements AfterViewInit {
public constructor(@Inject(ActivatedRoute) public readonly route: ActivatedRoute) {
this.tableData$ = this.tableDataSubject.asObservable();
this.addTableRows(10);
-
- this.comboboxItems = [];
- for (let i = 0; i < 300; i++) {
- this.comboboxItems.push({
- first: i.toString(),
- last: i.toString()
- });
- }
}
public ngAfterViewInit(): void {
diff --git a/angular-workspace/projects/ni/nimble-angular/CHANGELOG.json b/angular-workspace/projects/ni/nimble-angular/CHANGELOG.json
index 0a5bf2ef65..cf827b3be0 100644
--- a/angular-workspace/projects/ni/nimble-angular/CHANGELOG.json
+++ b/angular-workspace/projects/ni/nimble-angular/CHANGELOG.json
@@ -1,6 +1,21 @@
{
"name": "@ni/nimble-angular",
"entries": [
+ {
+ "date": "Tue, 12 Mar 2024 21:01:54 GMT",
+ "version": "20.2.21",
+ "tag": "@ni/nimble-angular_v20.2.21",
+ "comments": {
+ "patch": [
+ {
+ "author": "beachball",
+ "package": "@ni/nimble-angular",
+ "comment": "Bump @ni/nimble-components to v21.10.1",
+ "commit": "not available"
+ }
+ ]
+ }
+ },
{
"date": "Thu, 07 Mar 2024 21:20:52 GMT",
"version": "20.2.20",
diff --git a/angular-workspace/projects/ni/nimble-angular/CHANGELOG.md b/angular-workspace/projects/ni/nimble-angular/CHANGELOG.md
index 343cd1ed90..dd7a77bf31 100644
--- a/angular-workspace/projects/ni/nimble-angular/CHANGELOG.md
+++ b/angular-workspace/projects/ni/nimble-angular/CHANGELOG.md
@@ -1,9 +1,17 @@
# Change Log - @ni/nimble-angular
-This log was last generated on Thu, 07 Mar 2024 21:20:52 GMT and should not be manually modified.
+This log was last generated on Tue, 12 Mar 2024 21:01:54 GMT and should not be manually modified.
+## 20.2.21
+
+Tue, 12 Mar 2024 21:01:54 GMT
+
+### Patches
+
+- Bump @ni/nimble-components to v21.10.1
+
## 20.2.20
Thu, 07 Mar 2024 21:20:52 GMT
diff --git a/angular-workspace/projects/ni/nimble-angular/package.json b/angular-workspace/projects/ni/nimble-angular/package.json
index c784c92f79..6ee91ca648 100644
--- a/angular-workspace/projects/ni/nimble-angular/package.json
+++ b/angular-workspace/projects/ni/nimble-angular/package.json
@@ -1,6 +1,6 @@
{
"name": "@ni/nimble-angular",
- "version": "20.2.20",
+ "version": "20.2.21",
"description": "Angular components for the NI Nimble Design System",
"scripts": {
"invoke-publish": "cd ../../../ && npm run build:library && cd dist/ni/nimble-angular && npm publish"
@@ -31,7 +31,7 @@
"@angular/forms": "^15.2.10",
"@angular/localize": "^15.2.10",
"@angular/router": "^15.2.10",
- "@ni/nimble-components": "^21.10.0"
+ "@ni/nimble-components": "^21.10.1"
},
"dependencies": {
"tslib": "^2.2.0"
diff --git a/angular-workspace/projects/ni/nimble-angular/select/testing/ng-package.json b/angular-workspace/projects/ni/nimble-angular/select/testing/ng-package.json
new file mode 100644
index 0000000000..e5440110fb
--- /dev/null
+++ b/angular-workspace/projects/ni/nimble-angular/select/testing/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "public-api.ts"
+ }
+}
\ No newline at end of file
diff --git a/angular-workspace/projects/ni/nimble-angular/select/testing/public-api.ts b/angular-workspace/projects/ni/nimble-angular/select/testing/public-api.ts
new file mode 100644
index 0000000000..5d27bfa639
--- /dev/null
+++ b/angular-workspace/projects/ni/nimble-angular/select/testing/public-api.ts
@@ -0,0 +1 @@
+export * from './select.pageobject';
\ No newline at end of file
diff --git a/angular-workspace/projects/ni/nimble-angular/select/testing/select.pageobject.ts b/angular-workspace/projects/ni/nimble-angular/select/testing/select.pageobject.ts
new file mode 100644
index 0000000000..9ed5c93fbe
--- /dev/null
+++ b/angular-workspace/projects/ni/nimble-angular/select/testing/select.pageobject.ts
@@ -0,0 +1,3 @@
+import { SelectPageObject } from '@ni/nimble-components/dist/esm/select/testing/select.pageobject';
+
+export { SelectPageObject };
\ No newline at end of file
diff --git a/angular-workspace/projects/ni/nimble-angular/src/directives/list-option/nimble-list-option.directive.ts b/angular-workspace/projects/ni/nimble-angular/src/directives/list-option/nimble-list-option.directive.ts
index 347d26335d..3dc2eb2c96 100644
--- a/angular-workspace/projects/ni/nimble-angular/src/directives/list-option/nimble-list-option.directive.ts
+++ b/angular-workspace/projects/ni/nimble-angular/src/directives/list-option/nimble-list-option.directive.ts
@@ -25,6 +25,22 @@ export class NimbleListOptionDirective {
this.renderer.setProperty(this.elementRef.nativeElement, 'disabled', toBooleanProperty(value));
}
+ public get selected(): boolean {
+ return this.elementRef.nativeElement.selected;
+ }
+
+ @Input() public set selected(value: BooleanValueOrAttribute) {
+ this.renderer.setProperty(this.elementRef.nativeElement, 'selected', toBooleanProperty(value));
+ }
+
+ public get hidden(): boolean {
+ return this.elementRef.nativeElement.hidden;
+ }
+
+ @Input() public set hidden(value: BooleanValueOrAttribute) {
+ this.renderer.setProperty(this.elementRef.nativeElement, 'hidden', toBooleanProperty(value));
+ }
+
public constructor(
private readonly elementRef: ElementRef,
private readonly renderer: Renderer2,
diff --git a/angular-workspace/projects/ni/nimble-angular/src/directives/list-option/tests/nimble-list-option.directive.spec.ts b/angular-workspace/projects/ni/nimble-angular/src/directives/list-option/tests/nimble-list-option.directive.spec.ts
index 73d0119148..fb96382d86 100644
--- a/angular-workspace/projects/ni/nimble-angular/src/directives/list-option/tests/nimble-list-option.directive.spec.ts
+++ b/angular-workspace/projects/ni/nimble-angular/src/directives/list-option/tests/nimble-list-option.directive.spec.ts
@@ -48,6 +48,16 @@ describe('Nimble listbox option', () => {
expect(directive.disabled).toBeUndefined();
expect(nativeElement.disabled).toBeUndefined();
});
+
+ it('has expected defaults for selected', () => {
+ expect(directive.selected).toBeFalse();
+ expect(nativeElement.selected).toBeFalse();
+ });
+
+ it('has expected defaults for hidden', () => {
+ expect(directive.hidden).toBeFalse();
+ expect(nativeElement.hidden).toBeFalse();
+ });
});
describe('with template string values', () => {
@@ -55,6 +65,8 @@ describe('Nimble listbox option', () => {
template: `
`
})
@@ -82,6 +94,16 @@ describe('Nimble listbox option', () => {
expect(directive.disabled).toBeTrue();
expect(nativeElement.disabled).toBeTrue();
});
+
+ it('will use template string values for selected', () => {
+ expect(directive.selected).toBeTrue();
+ expect(nativeElement.selected).toBeTrue();
+ });
+
+ it('will use template string values for hidden', () => {
+ expect(directive.hidden).toBeTrue();
+ expect(nativeElement.hidden).toBeTrue();
+ });
});
describe('with property bound values', () => {
@@ -89,6 +111,8 @@ describe('Nimble listbox option', () => {
template: `
`
})
@@ -97,6 +121,8 @@ describe('Nimble listbox option', () => {
@ViewChild('listOption', { read: ElementRef }) public elementRef: ElementRef;
public disabled = false;
+ public selected = false;
+ public hidden = false;
}
let fixture: ComponentFixture;
@@ -124,13 +150,37 @@ describe('Nimble listbox option', () => {
expect(directive.disabled).toBeTrue();
expect(nativeElement.disabled).toBeTrue();
});
+
+ it('can be configured with property binding for selected', () => {
+ expect(directive.selected).toBeFalse();
+ expect(nativeElement.selected).toBeFalse();
+
+ fixture.componentInstance.selected = true;
+ fixture.detectChanges();
+
+ expect(directive.selected).toBeTrue();
+ expect(nativeElement.selected).toBeTrue();
+ });
+
+ it('can be configured with property binding for hidden', () => {
+ expect(directive.hidden).toBeFalse();
+ expect(nativeElement.hidden).toBeFalse();
+
+ fixture.componentInstance.hidden = true;
+ fixture.detectChanges();
+
+ expect(directive.hidden).toBeTrue();
+ expect(nativeElement.hidden).toBeTrue();
+ });
});
describe('with property attribute values', () => {
@Component({
template: `
+ [attr.disabled]="disabled"
+ [attr.selected]="selected"
+ [attr.hidden]="hidden">
`
})
@@ -139,6 +189,8 @@ describe('Nimble listbox option', () => {
@ViewChild('listOption', { read: ElementRef }) public elementRef: ElementRef;
public disabled: BooleanValueOrAttribute = null;
+ public selected: BooleanValueOrAttribute = null;
+ public hidden: BooleanValueOrAttribute = null;
}
let fixture: ComponentFixture;
@@ -166,5 +218,27 @@ describe('Nimble listbox option', () => {
expect(directive.disabled).toBeTrue();
expect(nativeElement.disabled).toBeTrue();
});
+
+ it('can be configured with attribute binding for selected', () => {
+ expect(directive.selected).toBeFalse();
+ expect(nativeElement.selected).toBeFalse();
+
+ fixture.componentInstance.selected = '';
+ fixture.detectChanges();
+
+ expect(directive.selected).toBeTrue();
+ expect(nativeElement.selected).toBeTrue();
+ });
+
+ it('can be configured with attribute binding for hidden', () => {
+ expect(directive.hidden).toBeFalse();
+ expect(nativeElement.hidden).toBeFalse();
+
+ fixture.componentInstance.hidden = '';
+ fixture.detectChanges();
+
+ expect(directive.hidden).toBeTrue();
+ expect(nativeElement.hidden).toBeTrue();
+ });
});
});
diff --git a/angular-workspace/projects/ni/nimble-angular/src/directives/select/tests/nimble-select-control-value-accessor.directive.spec.ts b/angular-workspace/projects/ni/nimble-angular/src/directives/select/tests/nimble-select-control-value-accessor.directive.spec.ts
index 504d50f192..250ec6d45c 100644
--- a/angular-workspace/projects/ni/nimble-angular/src/directives/select/tests/nimble-select-control-value-accessor.directive.spec.ts
+++ b/angular-workspace/projects/ni/nimble-angular/src/directives/select/tests/nimble-select-control-value-accessor.directive.spec.ts
@@ -1,16 +1,12 @@
-import { Component, ElementRef, ViewChild } from '@angular/core';
+import { Component, ElementRef, Input, OnChanges, OnInit, SimpleChange, SimpleChanges, ViewChild } from '@angular/core';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
-import { FormsModule } from '@angular/forms';
+import { AbstractControl, FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { SelectPageObject } from '@ni/nimble-angular/select/testing';
import { NimbleSelectModule } from '../nimble-select.module';
import { NimbleListOptionModule } from '../../list-option/nimble-list-option.module';
import { processUpdates, waitForUpdatesAsync } from '../../../testing/async-helpers';
import type { Select } from '../nimble-select.directive';
-function setSelectValue(select: Select, index: number): void {
- select.dispatchEvent(new Event('click'));
- select.options[index].dispatchEvent(new Event('click', { bubbles: true }));
-}
-
describe('Nimble select control value accessor', () => {
describe('when using option\'s [ngValue] binding', () => {
@Component({
@@ -46,6 +42,7 @@ describe('Nimble select control value accessor', () => {
let select: Select;
let fixture: ComponentFixture;
let testHostComponent: TestHostComponent;
+ let pageObject: SelectPageObject;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -58,6 +55,7 @@ describe('Nimble select control value accessor', () => {
fixture = TestBed.createComponent(TestHostComponent);
testHostComponent = fixture.componentInstance;
select = testHostComponent.select.nativeElement;
+ pageObject = new SelectPageObject(select);
fixture.detectChanges();
// wait for select's 'options' property to be updated from slotted content
await waitForUpdatesAsync();
@@ -81,7 +79,8 @@ describe('Nimble select control value accessor', () => {
}));
it('updates bound property when selected value is changed', () => {
- setSelectValue(select, 2);
+ pageObject.clickSelect();
+ pageObject.clickOption(2);
fixture.detectChanges();
expect(testHostComponent.selectedOption).toBe(testHostComponent.selectOptions[2]);
@@ -110,7 +109,8 @@ describe('Nimble select control value accessor', () => {
it('fires ngModelChange one time with expected value', () => {
const ngModelChangeSpy = spyOn(testHostComponent, 'onModelValueChange');
const indexToSelect = 2;
- setSelectValue(select, indexToSelect);
+ pageObject.clickSelect();
+ pageObject.clickOption(indexToSelect);
fixture.detectChanges();
expect(ngModelChangeSpy).toHaveBeenCalledOnceWith(testHostComponent.selectOptions[indexToSelect]);
});
@@ -142,6 +142,7 @@ describe('Nimble select control value accessor', () => {
let select: Select;
let fixture: ComponentFixture;
let testHostComponent: TestHostComponent;
+ let pageObject: SelectPageObject;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -154,6 +155,7 @@ describe('Nimble select control value accessor', () => {
fixture = TestBed.createComponent(TestHostComponent);
testHostComponent = fixture.componentInstance;
select = testHostComponent.select.nativeElement;
+ pageObject = new SelectPageObject(select);
fixture.detectChanges();
// wait for select's 'options' property to be updated from slotted content
await waitForUpdatesAsync();
@@ -177,10 +179,161 @@ describe('Nimble select control value accessor', () => {
}));
it('updates bound property when selected value is changed', () => {
- setSelectValue(select, 2);
+ pageObject.clickSelect();
+ pageObject.clickOption(2);
fixture.detectChanges();
expect(testHostComponent.selectedOption).toBe(testHostComponent.selectOptions[2].value.toString());
});
});
+
+ describe('dynamically added options on init', () => {
+ @Component({
+ template: `
+
+ `
+ })
+ class TestHostComponent implements OnInit {
+ @ViewChild('select', { static: true }) public select: ElementRef