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
+ Option 1 Option 2 Option 3 + Option 1 Option 2 Option 3 + 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..fc72ae7a4e 100644 --- a/angular-workspace/projects/ni/nimble-angular/CHANGELOG.json +++ b/angular-workspace/projects/ni/nimble-angular/CHANGELOG.json @@ -1,6 +1,78 @@ { "name": "@ni/nimble-angular", "entries": [ + { + "date": "Thu, 14 Mar 2024 14:48:16 GMT", + "version": "20.4.1", + "tag": "@ni/nimble-angular_v20.4.1", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@ni/nimble-angular", + "comment": "Bump @ni/nimble-components to v22.0.1", + "commit": "not available" + } + ] + } + }, + { + "date": "Wed, 13 Mar 2024 23:07:24 GMT", + "version": "20.4.0", + "tag": "@ni/nimble-angular_v20.4.0", + "comments": { + "minor": [ + { + "author": "jattasNI@users.noreply.github.com", + "package": "@ni/nimble-angular", + "commit": "518c229b964d2c4a2e77210692f5d367f2937ff4", + "comment": "Update default values in label provider directives and expose additional label provider properties" + }, + { + "author": "beachball", + "package": "@ni/nimble-angular", + "comment": "Bump @ni/nimble-components to v22.0.0", + "commit": "not available" + } + ] + } + }, + { + "date": "Tue, 12 Mar 2024 22:17:11 GMT", + "version": "20.3.0", + "tag": "@ni/nimble-angular_v20.3.0", + "comments": { + "minor": [ + { + "author": "26874831+atmgrifter00@users.noreply.github.com", + "package": "@ni/nimble-angular", + "commit": "880dfa1a7f1f9f478dc87199184dba3e5e2c0a56", + "comment": "Add selected and hidden attributes to NimbleListOptionDirective. Also exporting SelectPageObject." + }, + { + "author": "beachball", + "package": "@ni/nimble-angular", + "comment": "Bump @ni/nimble-components to v21.10.2", + "commit": "not available" + } + ] + } + }, + { + "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..4668075987 100644 --- a/angular-workspace/projects/ni/nimble-angular/CHANGELOG.md +++ b/angular-workspace/projects/ni/nimble-angular/CHANGELOG.md @@ -1,9 +1,43 @@ # 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 Thu, 14 Mar 2024 14:48:16 GMT and should not be manually modified. +## 20.4.1 + +Thu, 14 Mar 2024 14:48:16 GMT + +### Patches + +- Bump @ni/nimble-components to v22.0.1 + +## 20.4.0 + +Wed, 13 Mar 2024 23:07:24 GMT + +### Minor changes + +- Update default values in label provider directives and expose additional label provider properties ([ni/nimble@518c229](https://github.com/ni/nimble/commit/518c229b964d2c4a2e77210692f5d367f2937ff4)) +- Bump @ni/nimble-components to v22.0.0 + +## 20.3.0 + +Tue, 12 Mar 2024 22:17:11 GMT + +### Minor changes + +- Add selected and hidden attributes to NimbleListOptionDirective. Also exporting SelectPageObject. ([ni/nimble@880dfa1](https://github.com/ni/nimble/commit/880dfa1a7f1f9f478dc87199184dba3e5e2c0a56)) +- Bump @ni/nimble-components to v21.10.2 + +## 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/label-provider/core/nimble-label-provider-core-with-defaults.directive.ts b/angular-workspace/projects/ni/nimble-angular/label-provider/core/nimble-label-provider-core-with-defaults.directive.ts index bff9c0e24a..63d82cdbfb 100644 --- a/angular-workspace/projects/ni/nimble-angular/label-provider/core/nimble-label-provider-core-with-defaults.directive.ts +++ b/angular-workspace/projects/ni/nimble-angular/label-provider/core/nimble-label-provider-core-with-defaults.directive.ts @@ -15,5 +15,10 @@ export class NimbleLabelProviderCoreWithDefaultsDirective { this.elementRef.nativeElement.popupDismiss = $localize`:Nimble popup - dismiss|:Close`; this.elementRef.nativeElement.numericDecrement = $localize`:Nimble numeric - decrement|:Decrement`; this.elementRef.nativeElement.numericIncrement = $localize`:Nimble numeric - increment|:Increment`; + this.elementRef.nativeElement.popupIconError = $localize`:Nimble popup icon - error|:Error`; + this.elementRef.nativeElement.popupIconWarning = $localize`:Nimble popup icon - warning|:Warning`; + this.elementRef.nativeElement.popupIconInformation = $localize`:Nimble popup icon - information|:Information`; + this.elementRef.nativeElement.filterSearch = $localize`:Nimble select - search items|:Search`; + this.elementRef.nativeElement.filterNoResults = $localize`:Nimble select - no items|:No items found`; } } \ No newline at end of file diff --git a/angular-workspace/projects/ni/nimble-angular/label-provider/core/nimble-label-provider-core.directive.ts b/angular-workspace/projects/ni/nimble-angular/label-provider/core/nimble-label-provider-core.directive.ts index 6c6276d7bd..17fa20a4a7 100644 --- a/angular-workspace/projects/ni/nimble-angular/label-provider/core/nimble-label-provider-core.directive.ts +++ b/angular-workspace/projects/ni/nimble-angular/label-provider/core/nimble-label-provider-core.directive.ts @@ -37,4 +37,44 @@ export class NimbleLabelProviderCoreDirective { @Input('numeric-increment') public set numericIncrement(value: string | undefined) { this.renderer.setProperty(this.elementRef.nativeElement, 'numericIncrement', value); } + + public get popupIconError(): string | undefined { + return this.elementRef.nativeElement.popupIconError; + } + + @Input('popup-icon-error') public set popupIconError(value: string | undefined) { + this.renderer.setProperty(this.elementRef.nativeElement, 'popupIconError', value); + } + + public get popupIconWarning(): string | undefined { + return this.elementRef.nativeElement.popupIconWarning; + } + + @Input('popup-icon-warning') public set popupIconWarning(value: string | undefined) { + this.renderer.setProperty(this.elementRef.nativeElement, 'popupIconWarning', value); + } + + public get popupIconInformation(): string | undefined { + return this.elementRef.nativeElement.popupIconInformation; + } + + @Input('popup-icon-information') public set popupIconInformation(value: string | undefined) { + this.renderer.setProperty(this.elementRef.nativeElement, 'popupIconInformation', value); + } + + public get filterSearch(): string | undefined { + return this.elementRef.nativeElement.filterSearch; + } + + @Input('filter-search') public set filterSearch(value: string | undefined) { + this.renderer.setProperty(this.elementRef.nativeElement, 'filterSearch', value); + } + + public get filterNoResults(): string | undefined { + return this.elementRef.nativeElement.filterNoResults; + } + + @Input('filter-no-results') public set filterNoResults(value: string | undefined) { + this.renderer.setProperty(this.elementRef.nativeElement, 'filterNoResults', value); + } } \ No newline at end of file diff --git a/angular-workspace/projects/ni/nimble-angular/label-provider/core/tests/nimble-label-provider-core-with-defaults.directive.spec.ts b/angular-workspace/projects/ni/nimble-angular/label-provider/core/tests/nimble-label-provider-core-with-defaults.directive.spec.ts index 0b74b5a736..50a4b6bade 100644 --- a/angular-workspace/projects/ni/nimble-angular/label-provider/core/tests/nimble-label-provider-core-with-defaults.directive.spec.ts +++ b/angular-workspace/projects/ni/nimble-angular/label-provider/core/tests/nimble-label-provider-core-with-defaults.directive.spec.ts @@ -30,7 +30,12 @@ describe('Nimble LabelProviderCore withDefaults directive', () => { loadTranslations({ [computeMsgId('Close', 'Nimble popup - dismiss')]: 'Translated close', [computeMsgId('Decrement', 'Nimble numeric - decrement')]: 'Translated decrement', - [computeMsgId('Increment', 'Nimble numeric - increment')]: 'Translated increment' + [computeMsgId('Increment', 'Nimble numeric - increment')]: 'Translated increment', + [computeMsgId('Error', 'Nimble popup icon - error')]: 'Translated error', + [computeMsgId('Warning', 'Nimble popup icon - warning')]: 'Translated warning', + [computeMsgId('Information', 'Nimble popup icon - information')]: 'Translated information', + [computeMsgId('Search', 'Nimble select - search items')]: 'Translated search', + [computeMsgId('No items found', 'Nimble select - no items')]: 'Translated no items found' }); const fixture = TestBed.createComponent(TestHostComponent); const testHostComponent = fixture.componentInstance; @@ -42,5 +47,10 @@ describe('Nimble LabelProviderCore withDefaults directive', () => { expect(labelProvider.popupDismiss).toBe('Translated close'); expect(labelProvider.numericDecrement).toBe('Translated decrement'); expect(labelProvider.numericIncrement).toBe('Translated increment'); + expect(labelProvider.popupIconError).toBe('Translated error'); + expect(labelProvider.popupIconWarning).toBe('Translated warning'); + expect(labelProvider.popupIconInformation).toBe('Translated information'); + expect(labelProvider.filterSearch).toBe('Translated search'); + expect(labelProvider.filterNoResults).toBe('Translated no items found'); }); }); diff --git a/angular-workspace/projects/ni/nimble-angular/label-provider/core/tests/nimble-label-provider-core.directive.spec.ts b/angular-workspace/projects/ni/nimble-angular/label-provider/core/tests/nimble-label-provider-core.directive.spec.ts index 7a276c1b41..3249e7e4e4 100644 --- a/angular-workspace/projects/ni/nimble-angular/label-provider/core/tests/nimble-label-provider-core.directive.spec.ts +++ b/angular-workspace/projects/ni/nimble-angular/label-provider/core/tests/nimble-label-provider-core.directive.spec.ts @@ -7,6 +7,11 @@ describe('Nimble Label Provider Core', () => { const label1 = 'String 1'; const label2 = 'String 2'; const label3 = 'String 3'; + const label4 = 'String 4'; + const label5 = 'String 5'; + const label6 = 'String 6'; + const label7 = 'String 7'; + const label8 = 'String 8'; beforeEach(() => { TestBed.configureTestingModule({ @@ -58,6 +63,31 @@ describe('Nimble Label Provider Core', () => { expect(directive.numericIncrement).toBeUndefined(); expect(nativeElement.numericIncrement).toBeUndefined(); }); + + it('has expected defaults for popupIconError', () => { + expect(directive.popupIconError).toBeUndefined(); + expect(nativeElement.popupIconError).toBeUndefined(); + }); + + it('has expected defaults for popupIconWarning', () => { + expect(directive.popupIconWarning).toBeUndefined(); + expect(nativeElement.popupIconWarning).toBeUndefined(); + }); + + it('has expected defaults for popupIconInformation', () => { + expect(directive.popupIconInformation).toBeUndefined(); + expect(nativeElement.popupIconInformation).toBeUndefined(); + }); + + it('has expected defaults for filterSearch', () => { + expect(directive.filterSearch).toBeUndefined(); + expect(nativeElement.filterSearch).toBeUndefined(); + }); + + it('has expected defaults for filterNoResults', () => { + expect(directive.filterNoResults).toBeUndefined(); + expect(nativeElement.filterNoResults).toBeUndefined(); + }); }); describe('with template string values', () => { @@ -67,6 +97,11 @@ describe('Nimble Label Provider Core', () => { popup-dismiss="${label1}" numeric-decrement="${label2}" numeric-increment="${label3}" + popup-icon-error="${label4}" + popup-icon-warning="${label5}" + popup-icon-information="${label6}" + filter-search="${label7}" + filter-no-results="${label8}" > ` @@ -105,6 +140,31 @@ describe('Nimble Label Provider Core', () => { expect(directive.numericIncrement).toBe(label3); expect(nativeElement.numericIncrement).toBe(label3); }); + + it('will use template string values for popupIconError', () => { + expect(directive.popupIconError).toBe(label4); + expect(nativeElement.popupIconError).toBe(label4); + }); + + it('will use template string values for popupIconWarning', () => { + expect(directive.popupIconWarning).toBe(label5); + expect(nativeElement.popupIconWarning).toBe(label5); + }); + + it('will use template string values for popupIconInformation', () => { + expect(directive.popupIconInformation).toBe(label6); + expect(nativeElement.popupIconInformation).toBe(label6); + }); + + it('will use template string values for filterSearch', () => { + expect(directive.filterSearch).toBe(label7); + expect(nativeElement.filterSearch).toBe(label7); + }); + + it('will use template string values for filterNoResults', () => { + expect(directive.filterNoResults).toBe(label8); + expect(nativeElement.filterNoResults).toBe(label8); + }); }); describe('with property bound values', () => { @@ -114,6 +174,11 @@ describe('Nimble Label Provider Core', () => { [popupDismiss]="popupDismiss" [numericDecrement]="numericDecrement" [numericIncrement]="numericIncrement" + [popupIconError]="popupIconError" + [popupIconWarning]="popupIconWarning" + [popupIconInformation]="popupIconInformation" + [filterSearch]="filterSearch" + [filterNoResults]="filterNoResults" > ` @@ -124,6 +189,11 @@ describe('Nimble Label Provider Core', () => { public popupDismiss = label1; public numericDecrement = label1; public numericIncrement = label1; + public popupIconError = label1; + public popupIconWarning = label1; + public popupIconInformation = label1; + public filterSearch = label1; + public filterNoResults = label1; } let fixture: ComponentFixture; @@ -173,6 +243,61 @@ describe('Nimble Label Provider Core', () => { expect(directive.numericIncrement).toBe(label2); expect(nativeElement.numericIncrement).toBe(label2); }); + + it('can be configured with property binding for popupIconError', () => { + expect(directive.popupIconError).toBe(label1); + expect(nativeElement.popupIconError).toBe(label1); + + fixture.componentInstance.popupIconError = label2; + fixture.detectChanges(); + + expect(directive.popupIconError).toBe(label2); + expect(nativeElement.popupIconError).toBe(label2); + }); + + it('can be configured with property binding for popupIconWarning', () => { + expect(directive.popupIconWarning).toBe(label1); + expect(nativeElement.popupIconWarning).toBe(label1); + + fixture.componentInstance.popupIconWarning = label2; + fixture.detectChanges(); + + expect(directive.popupIconWarning).toBe(label2); + expect(nativeElement.popupIconWarning).toBe(label2); + }); + + it('can be configured with property binding for popupIconInformation', () => { + expect(directive.popupIconInformation).toBe(label1); + expect(nativeElement.popupIconInformation).toBe(label1); + + fixture.componentInstance.popupIconInformation = label2; + fixture.detectChanges(); + + expect(directive.popupIconInformation).toBe(label2); + expect(nativeElement.popupIconInformation).toBe(label2); + }); + + it('can be configured with property binding for filterSearch', () => { + expect(directive.filterSearch).toBe(label1); + expect(nativeElement.filterSearch).toBe(label1); + + fixture.componentInstance.filterSearch = label2; + fixture.detectChanges(); + + expect(directive.filterSearch).toBe(label2); + expect(nativeElement.filterSearch).toBe(label2); + }); + + it('can be configured with property binding for filterNoResults', () => { + expect(directive.filterNoResults).toBe(label1); + expect(nativeElement.filterNoResults).toBe(label1); + + fixture.componentInstance.filterNoResults = label2; + fixture.detectChanges(); + + expect(directive.filterNoResults).toBe(label2); + expect(nativeElement.filterNoResults).toBe(label2); + }); }); describe('with attribute bound values', () => { @@ -182,6 +307,11 @@ describe('Nimble Label Provider Core', () => { [attr.popup-dismiss]="popupDismiss" [attr.numeric-decrement]="numericDecrement" [attr.numeric-increment]="numericIncrement" + [attr.popup-icon-error]="popupIconError" + [attr.popup-icon-warning]="popupIconWarning" + [attr.popup-icon-information]="popupIconInformation" + [attr.filter-search]="filterSearch" + [attr.filter-no-results]="filterNoResults" > ` @@ -192,6 +322,11 @@ describe('Nimble Label Provider Core', () => { public popupDismiss = label1; public numericDecrement = label1; public numericIncrement = label1; + public popupIconError = label1; + public popupIconWarning = label1; + public popupIconInformation = label1; + public filterSearch = label1; + public filterNoResults = label1; } let fixture: ComponentFixture; @@ -241,5 +376,60 @@ describe('Nimble Label Provider Core', () => { expect(directive.numericIncrement).toBe(label2); expect(nativeElement.numericIncrement).toBe(label2); }); + + it('can be configured with attribute binding for popupIconError', () => { + expect(directive.popupIconError).toBe(label1); + expect(nativeElement.popupIconError).toBe(label1); + + fixture.componentInstance.popupIconError = label2; + fixture.detectChanges(); + + expect(directive.popupIconError).toBe(label2); + expect(nativeElement.popupIconError).toBe(label2); + }); + + it('can be configured with attribute binding for popupIconWarning', () => { + expect(directive.popupIconWarning).toBe(label1); + expect(nativeElement.popupIconWarning).toBe(label1); + + fixture.componentInstance.popupIconWarning = label2; + fixture.detectChanges(); + + expect(directive.popupIconWarning).toBe(label2); + expect(nativeElement.popupIconWarning).toBe(label2); + }); + + it('can be configured with attribute binding for popupIconInformation', () => { + expect(directive.popupIconInformation).toBe(label1); + expect(nativeElement.popupIconInformation).toBe(label1); + + fixture.componentInstance.popupIconInformation = label2; + fixture.detectChanges(); + + expect(directive.popupIconInformation).toBe(label2); + expect(nativeElement.popupIconInformation).toBe(label2); + }); + + it('can be configured with attribute binding for filterSearch', () => { + expect(directive.filterSearch).toBe(label1); + expect(nativeElement.filterSearch).toBe(label1); + + fixture.componentInstance.filterSearch = label2; + fixture.detectChanges(); + + expect(directive.filterSearch).toBe(label2); + expect(nativeElement.filterSearch).toBe(label2); + }); + + it('can be configured with attribute binding for filterNoResults', () => { + expect(directive.filterNoResults).toBe(label1); + expect(nativeElement.filterNoResults).toBe(label1); + + fixture.componentInstance.filterNoResults = label2; + fixture.detectChanges(); + + expect(directive.filterNoResults).toBe(label2); + expect(nativeElement.filterNoResults).toBe(label2); + }); }); }); diff --git a/angular-workspace/projects/ni/nimble-angular/label-provider/table/nimble-label-provider-table-with-defaults.directive.ts b/angular-workspace/projects/ni/nimble-angular/label-provider/table/nimble-label-provider-table-with-defaults.directive.ts index cbee2fcd95..4c4d2977b6 100644 --- a/angular-workspace/projects/ni/nimble-angular/label-provider/table/nimble-label-provider-table-with-defaults.directive.ts +++ b/angular-workspace/projects/ni/nimble-angular/label-provider/table/nimble-label-provider-table-with-defaults.directive.ts @@ -14,6 +14,8 @@ export class NimbleLabelProviderTableWithDefaultsDirective { public constructor(protected readonly renderer: Renderer2, protected readonly elementRef: ElementRef) { this.elementRef.nativeElement.groupCollapse = $localize`:Nimble table - collapse group|:Collapse group`; this.elementRef.nativeElement.groupExpand = $localize`:Nimble table - expand group|:Expand group`; + this.elementRef.nativeElement.rowCollapse = $localize`:Nimble table - collapse row|:Collapse row`; + this.elementRef.nativeElement.rowExpand = $localize`:Nimble table - expand row|:Expand row`; this.elementRef.nativeElement.collapseAll = $localize`:Nimble table - collapse all|:Collapse all`; this.elementRef.nativeElement.cellActionMenu = $localize`:Nimble table - cell action menu|:Options`; this.elementRef.nativeElement.columnHeaderGrouped = $localize`:Nimble table - column header grouped|:Grouped`; diff --git a/angular-workspace/projects/ni/nimble-angular/label-provider/table/nimble-label-provider-table.directive.ts b/angular-workspace/projects/ni/nimble-angular/label-provider/table/nimble-label-provider-table.directive.ts index 7f68fe9cb5..ebf09e6e20 100644 --- a/angular-workspace/projects/ni/nimble-angular/label-provider/table/nimble-label-provider-table.directive.ts +++ b/angular-workspace/projects/ni/nimble-angular/label-provider/table/nimble-label-provider-table.directive.ts @@ -30,6 +30,22 @@ export class NimbleLabelProviderTableDirective { this.renderer.setProperty(this.elementRef.nativeElement, 'groupExpand', value); } + public get rowCollapse(): string | undefined { + return this.elementRef.nativeElement.rowCollapse; + } + + @Input('row-collapse') public set rowCollapse(value: string | undefined) { + this.renderer.setProperty(this.elementRef.nativeElement, 'rowCollapse', value); + } + + public get rowExpand(): string | undefined { + return this.elementRef.nativeElement.rowExpand; + } + + @Input('row-expand') public set rowExpand(value: string | undefined) { + this.renderer.setProperty(this.elementRef.nativeElement, 'rowExpand', value); + } + public get collapseAll(): string | undefined { return this.elementRef.nativeElement.collapseAll; } diff --git a/angular-workspace/projects/ni/nimble-angular/label-provider/table/tests/nimble-label-provider-table-with-defaults.directive.spec.ts b/angular-workspace/projects/ni/nimble-angular/label-provider/table/tests/nimble-label-provider-table-with-defaults.directive.spec.ts index 6f62cb10b9..1f72c8079a 100644 --- a/angular-workspace/projects/ni/nimble-angular/label-provider/table/tests/nimble-label-provider-table-with-defaults.directive.spec.ts +++ b/angular-workspace/projects/ni/nimble-angular/label-provider/table/tests/nimble-label-provider-table-with-defaults.directive.spec.ts @@ -30,6 +30,8 @@ describe('Nimble LabelProviderTable withDefaults directive', () => { loadTranslations({ [computeMsgId('Collapse group', 'Nimble table - collapse group')]: 'Translated collapse group', [computeMsgId('Expand group', 'Nimble table - expand group')]: 'Translated expand group', + [computeMsgId('Collapse row', 'Nimble table - collapse row')]: 'Translated collapse row', + [computeMsgId('Expand row', 'Nimble table - expand row')]: 'Translated expand row', [computeMsgId('Collapse all', 'Nimble table - collapse all')]: 'Translated collapse all', [computeMsgId('Options', 'Nimble table - cell action menu')]: 'Translated options', [computeMsgId('Grouped', 'Nimble table - column header grouped')]: 'Translated grouped', @@ -50,6 +52,8 @@ describe('Nimble LabelProviderTable withDefaults directive', () => { it('applies translated values for each label', () => { expect(labelProvider.groupCollapse).toBe('Translated collapse group'); expect(labelProvider.groupExpand).toBe('Translated expand group'); + expect(labelProvider.rowCollapse).toBe('Translated collapse row'); + expect(labelProvider.rowExpand).toBe('Translated expand row'); expect(labelProvider.collapseAll).toBe('Translated collapse all'); expect(labelProvider.cellActionMenu).toBe('Translated options'); expect(labelProvider.columnHeaderGrouped).toBe('Translated grouped'); diff --git a/angular-workspace/projects/ni/nimble-angular/label-provider/table/tests/nimble-label-provider-table.directive.spec.ts b/angular-workspace/projects/ni/nimble-angular/label-provider/table/tests/nimble-label-provider-table.directive.spec.ts index 87f25ad84c..e0b96f47c4 100644 --- a/angular-workspace/projects/ni/nimble-angular/label-provider/table/tests/nimble-label-provider-table.directive.spec.ts +++ b/angular-workspace/projects/ni/nimble-angular/label-provider/table/tests/nimble-label-provider-table.directive.spec.ts @@ -16,6 +16,8 @@ describe('Nimble Label Provider Table', () => { const label10 = 'String 10'; const label11 = 'String 11'; const label12 = 'String 12'; + const label13 = 'String 13'; + const label14 = 'String 14'; beforeEach(() => { TestBed.configureTestingModule({ @@ -73,6 +75,16 @@ describe('Nimble Label Provider Table', () => { expect(nativeElement.groupExpand).toBeUndefined(); }); + it('has expected defaults for rowCollapse', () => { + expect(directive.rowCollapse).toBeUndefined(); + expect(nativeElement.rowCollapse).toBeUndefined(); + }); + + it('has expected defaults for rowExpand', () => { + expect(directive.rowExpand).toBeUndefined(); + expect(nativeElement.rowExpand).toBeUndefined(); + }); + it('has expected defaults for collapseAll', () => { expect(directive.collapseAll).toBeUndefined(); expect(nativeElement.collapseAll).toBeUndefined(); @@ -122,14 +134,16 @@ describe('Nimble Label Provider Table', () => { column-header-grouped="${label2}" group-collapse="${label3}" group-expand="${label4}" - collapse-all="${label5}" - column-header-sorted-ascending="${label6}" - column-header-sorted-descending="${label7}" - select-all="${label8}" - group-select-all="${label9}" - row-select="${label10}" - row-operation-column="${label11}" - row-loading="${label12}" + row-collapse="${label5}" + row-expand="${label6}" + collapse-all="${label7}" + column-header-sorted-ascending="${label8}" + column-header-sorted-descending="${label9}" + select-all="${label10}" + group-select-all="${label11}" + row-select="${label12}" + row-operation-column="${label13}" + row-loading="${label14}" > ` @@ -174,44 +188,54 @@ describe('Nimble Label Provider Table', () => { expect(nativeElement.groupExpand).toBe(label4); }); + it('will use template string values for rowCollapse', () => { + expect(directive.rowCollapse).toBe(label5); + expect(nativeElement.rowCollapse).toBe(label5); + }); + + it('will use template string values for rowExpand', () => { + expect(directive.rowExpand).toBe(label6); + expect(nativeElement.rowExpand).toBe(label6); + }); + it('will use template string values for collapseAll', () => { - expect(directive.collapseAll).toBe(label5); - expect(nativeElement.collapseAll).toBe(label5); + expect(directive.collapseAll).toBe(label7); + expect(nativeElement.collapseAll).toBe(label7); }); it('will use template string values for columnHeaderSortedAscending', () => { - expect(directive.columnHeaderSortedAscending).toBe(label6); - expect(nativeElement.columnHeaderSortedAscending).toBe(label6); + expect(directive.columnHeaderSortedAscending).toBe(label8); + expect(nativeElement.columnHeaderSortedAscending).toBe(label8); }); it('will use template string values for columnHeaderSortedDescending', () => { - expect(directive.columnHeaderSortedDescending).toBe(label7); - expect(nativeElement.columnHeaderSortedDescending).toBe(label7); + expect(directive.columnHeaderSortedDescending).toBe(label9); + expect(nativeElement.columnHeaderSortedDescending).toBe(label9); }); it('will use template string values for selectAll', () => { - expect(directive.selectAll).toBe(label8); - expect(nativeElement.selectAll).toBe(label8); + expect(directive.selectAll).toBe(label10); + expect(nativeElement.selectAll).toBe(label10); }); it('will use template string values for groupSelectAll', () => { - expect(directive.groupSelectAll).toBe(label9); - expect(nativeElement.groupSelectAll).toBe(label9); + expect(directive.groupSelectAll).toBe(label11); + expect(nativeElement.groupSelectAll).toBe(label11); }); it('will use template string values for rowSelect', () => { - expect(directive.rowSelect).toBe(label10); - expect(nativeElement.rowSelect).toBe(label10); + expect(directive.rowSelect).toBe(label12); + expect(nativeElement.rowSelect).toBe(label12); }); it('will use template string values for rowOperationColumn', () => { - expect(directive.rowOperationColumn).toBe(label11); - expect(nativeElement.rowOperationColumn).toBe(label11); + expect(directive.rowOperationColumn).toBe(label13); + expect(nativeElement.rowOperationColumn).toBe(label13); }); it('will use template string values for rowLoading', () => { - expect(directive.rowLoading).toBe(label12); - expect(nativeElement.rowLoading).toBe(label12); + expect(directive.rowLoading).toBe(label14); + expect(nativeElement.rowLoading).toBe(label14); }); }); @@ -223,6 +247,8 @@ describe('Nimble Label Provider Table', () => { [columnHeaderGrouped]="columnHeaderGrouped" [groupCollapse]="groupCollapse" [groupExpand]="groupExpand" + [rowCollapse]="rowCollapse" + [rowExpand]="rowExpand" [collapseAll]="collapseAll" [columnHeaderSortedAscending]="columnHeaderSortedAscending" [columnHeaderSortedDescending]="columnHeaderSortedDescending" @@ -242,6 +268,8 @@ describe('Nimble Label Provider Table', () => { public columnHeaderGrouped = label1; public groupCollapse = label1; public groupExpand = label1; + public rowCollapse = label1; + public rowExpand = label1; public collapseAll = label1; public columnHeaderSortedAscending = label1; public columnHeaderSortedDescending = label1; @@ -311,6 +339,28 @@ describe('Nimble Label Provider Table', () => { expect(nativeElement.groupExpand).toBe(label2); }); + it('can be configured with property binding for rowCollapse', () => { + expect(directive.rowCollapse).toBe(label1); + expect(nativeElement.rowCollapse).toBe(label1); + + fixture.componentInstance.rowCollapse = label2; + fixture.detectChanges(); + + expect(directive.rowCollapse).toBe(label2); + expect(nativeElement.rowCollapse).toBe(label2); + }); + + it('can be configured with property binding for rowExpand', () => { + expect(directive.rowExpand).toBe(label1); + expect(nativeElement.rowExpand).toBe(label1); + + fixture.componentInstance.rowExpand = label2; + fixture.detectChanges(); + + expect(directive.rowExpand).toBe(label2); + expect(nativeElement.rowExpand).toBe(label2); + }); + it('can be configured with property binding for collapseAll', () => { expect(directive.collapseAll).toBe(label1); expect(nativeElement.collapseAll).toBe(label1); @@ -408,6 +458,8 @@ describe('Nimble Label Provider Table', () => { [attr.column-header-grouped]="columnHeaderGrouped" [attr.group-collapse]="groupCollapse" [attr.group-expand]="groupExpand" + [attr.row-collapse]="rowCollapse" + [attr.row-expand]="rowExpand" [attr.collapse-all]="collapseAll" [attr.column-header-sorted-ascending]="columnHeaderSortedAscending" [attr.column-header-sorted-descending]="columnHeaderSortedDescending" @@ -427,6 +479,8 @@ describe('Nimble Label Provider Table', () => { public columnHeaderGrouped = label1; public groupCollapse = label1; public groupExpand = label1; + public rowCollapse = label1; + public rowExpand = label1; public collapseAll = label1; public columnHeaderSortedAscending = label1; public columnHeaderSortedDescending = label1; @@ -496,6 +550,28 @@ describe('Nimble Label Provider Table', () => { expect(nativeElement.groupExpand).toBe(label2); }); + it('can be configured with attribute binding for rowCollapse', () => { + expect(directive.rowCollapse).toBe(label1); + expect(nativeElement.rowCollapse).toBe(label1); + + fixture.componentInstance.rowCollapse = label2; + fixture.detectChanges(); + + expect(directive.rowCollapse).toBe(label2); + expect(nativeElement.rowCollapse).toBe(label2); + }); + + it('can be configured with attribute binding for rowExpand', () => { + expect(directive.rowExpand).toBe(label1); + expect(nativeElement.rowExpand).toBe(label1); + + fixture.componentInstance.rowExpand = label2; + fixture.detectChanges(); + + expect(directive.rowExpand).toBe(label2); + expect(nativeElement.rowExpand).toBe(label2); + }); + it('can be configured with attribute binding for collapseAll', () => { expect(directive.collapseAll).toBe(label1); expect(nativeElement.collapseAll).toBe(label1); diff --git a/angular-workspace/projects/ni/nimble-angular/package.json b/angular-workspace/projects/ni/nimble-angular/package.json index c784c92f79..27fb5bfdf7 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.4.1", "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": "^22.0.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: ` +
+ + + {{ option.name }} + + +
+ ` + }) + class TestHostComponent implements OnInit { + @ViewChild('select', { static: true }) public select: ElementRef; + + @Input() public selectValue?: { name: string, value: number }; + + public selectOptions: { name: string, value: number }[] = [ + { name: 'Option 1', value: 1 }, + { name: 'Option 2', value: 2 }, + { name: 'Option 3', value: 3 } + ]; + + public selectedOption = new FormControl<{ name: string, value: number } | null>(null); + + public form: FormGroup = new FormGroup({ + selectValue: this.selectedOption + }); + + public get selectValueField(): AbstractControl { + return this.form.get('selectValue')!; + } + + public ngOnChanges(changes: SimpleChanges): void { + if (changes.selectValue.currentValue !== changes.selectValue.previousValue) { + // intentionally adding option after first option for DOM order + const newValue = changes.selectValue.currentValue as { name: string, value: number }; + this.selectOptions.splice(1, 0, newValue); + this.selectValueField.setValue(newValue); + } + } + } + + let select: Select; + let fixture: ComponentFixture; + let testHostComponent: TestHostComponent; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [TestHostComponent], + imports: [NimbleSelectModule, NimbleListOptionModule, FormsModule, ReactiveFormsModule] + }); + }); + + beforeEach(async () => { + fixture = TestBed.createComponent(TestHostComponent); + testHostComponent = fixture.componentInstance; + select = testHostComponent.select.nativeElement; + fixture.detectChanges(); + // wait for select's 'options' property to be updated from slotted content + await waitForUpdatesAsync(); + }); + + afterEach(() => { + processUpdates(); + }); + + it('can set value to option that is dynamically added from input', async () => { + const oldSelectValue = testHostComponent.selectValue; + const newValue = { name: 'Option 4', value: 4 }; + testHostComponent.selectValue = newValue; + // this will result in an option added after the first option in DOM order + testHostComponent.ngOnChanges({ selectValue: new SimpleChange(oldSelectValue, testHostComponent.selectValue, false) }); + fixture.detectChanges(); + await fixture.whenStable(); + await waitForUpdatesAsync(); + expect(select.selectedIndex).toBe(1); + }); + }); }); diff --git a/package-lock.json b/package-lock.json index ab7a95c95e..c8e8dba934 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,12 +69,12 @@ "ng-packagr": "^15.2.2", "playwright": "1.40.0", "rollup": "^4.12.0", - "typescript": "~4.8.2" + "typescript": "~4.9.5" } }, "angular-workspace/projects/ni/nimble-angular": { "name": "@ni/nimble-angular", - "version": "20.2.20", + "version": "20.4.1", "license": "MIT", "dependencies": { "tslib": "^2.2.0" @@ -85,7 +85,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": "^22.0.1" } }, "node_modules/@11ty/dependency-tree": { @@ -6852,6 +6852,12 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -15543,6 +15549,22 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, "node_modules/configstore": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", @@ -17177,6 +17199,57 @@ "safer-buffer": "^2.1.0" } }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/editorconfig/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -22108,6 +22181,60 @@ "integrity": "sha512-CiRVjMKBUp6VYtGwzRjrdnro41yMwKGOWdP9ATXqmixdz2n7KHNwdqthTYSSbOKj/Ha79Gct1sA8ZQpse55TYA==", "dev": true }, + "node_modules/js-beautify": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz", + "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==", + "dev": true, + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.3.3", + "js-cookie": "^3.0.5", + "nopt": "^7.2.0" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-beautify/node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/js-beautify/node_modules/nopt": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", + "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", + "dev": true, + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/js-library-detector": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/js-library-detector/-/js-library-detector-6.7.0.tgz", @@ -27568,6 +27695,12 @@ "prosemirror-transform": "^1.1.0" } }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, "node_modules/protocols": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", @@ -30816,19 +30949,6 @@ "node": ">=8" } }, - "node_modules/svg-to-ts/node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, "node_modules/svg-to-ts/node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", @@ -31951,9 +32071,9 @@ } }, "node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -34048,7 +34168,7 @@ }, "packages/jasmine-parameterized": { "name": "@ni/jasmine-parameterized", - "version": "0.2.2", + "version": "0.2.3", "license": "MIT", "devDependencies": { "@ni/eslint-config-javascript": "^4.2.0", @@ -34061,12 +34181,12 @@ "karma-jasmine-html-reporter": "^2.0.0", "karma-spec-reporter": "^0.0.36", "playwright": "1.40.0", - "typescript": "~4.8.2" + "typescript": "~4.9.5" } }, "packages/nimble-blazor": { "name": "@ni/nimble-blazor", - "version": "14.3.18", + "version": "14.4.1", "hasInstallScript": true, "license": "MIT", "devDependencies": { @@ -34102,14 +34222,14 @@ }, "packages/nimble-components": { "name": "@ni/nimble-components", - "version": "21.10.0", + "version": "22.0.1", "license": "MIT", "dependencies": { "@microsoft/fast-colors": "^5.3.1", "@microsoft/fast-element": "^1.12.0", "@microsoft/fast-foundation": "2.49.4", "@microsoft/fast-web-utilities": "^6.0.0", - "@ni/nimble-tokens": "^6.12.0", + "@ni/nimble-tokens": "^6.12.1", "@tanstack/table-core": "^8.10.7", "@tanstack/virtual-core": "^3.0.0-beta.68", "@tiptap/core": "^2.2.2", @@ -34149,7 +34269,7 @@ "@microsoft/fast-react-wrapper": "0.3.22", "@ni/eslint-config-javascript": "^4.2.0", "@ni/eslint-config-typescript": "^4.2.0", - "@ni/jasmine-parameterized": "^0.2.2", + "@ni/jasmine-parameterized": "^0.2.3", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.1", @@ -34177,6 +34297,7 @@ "eslint-plugin-storybook": "^0.8.0", "html-webpack-plugin": "^5.3.1", "jasmine-core": "^5.1.2", + "js-beautify": "^1.15.1", "karma": "^6.3.0", "karma-chrome-launcher": "^3.1.0", "karma-firefox-launcher": "^2.1.0", @@ -34189,7 +34310,6 @@ "karma-webkit-launcher": "^2.1.0", "karma-webpack": "^5.0.0", "playwright": "1.40.0", - "prettier": "^3.2.5", "prettier-eslint": "^16.3.0", "prettier-eslint-cli": "^8.0.1", "remark-gfm": "^3.0.1", @@ -34199,7 +34319,7 @@ "source-map-loader": "^5.0.0", "storybook": "^7.6.13", "ts-loader": "^9.2.5", - "typescript": "~4.8.2", + "typescript": "~4.9.5", "webpack": "^5.75.0", "webpack-cli": "^5.0.1", "webpack-dev-middleware": "^7.0.0" @@ -35101,7 +35221,7 @@ }, "packages/nimble-tokens": { "name": "@ni/nimble-tokens", - "version": "6.12.0", + "version": "6.12.1", "license": "MIT", "devDependencies": { "@microsoft/fast-colors": "^5.3.1", @@ -35114,7 +35234,7 @@ "style-dictionary": "^3.9.2", "svg-to-ts": "^12.0.0", "to-ico": "^1.1.5", - "typescript": "~4.8.2" + "typescript": "~4.9.5" } }, "packages/nimble-tokens/node_modules/rimraf": { @@ -35143,7 +35263,7 @@ }, "devDependencies": { "@lhci/cli": "^0.13.0", - "typescript": "~4.8.2", + "typescript": "~4.9.5", "vite": "^5.1.5" } }, @@ -35155,13 +35275,13 @@ }, "devDependencies": { "@11ty/eleventy": "^2.0.1", - "typescript": "~4.8.2", + "typescript": "~4.9.5", "vite": "^5.1.5" } }, "packages/xliff-to-json-converter": { "name": "@ni/xliff-to-json-converter", - "version": "1.1.4", + "version": "1.1.5", "license": "MIT", "dependencies": { "xliff": "^6.1.0", @@ -35177,7 +35297,7 @@ "@types/yargs": "^17.0.10", "jasmine": "^5.1.0", "jasmine-core": "^5.1.2", - "typescript": "~4.8.2" + "typescript": "~4.9.5" } } } diff --git a/packages/jasmine-parameterized/CHANGELOG.json b/packages/jasmine-parameterized/CHANGELOG.json index 4a201f3b64..62aadb0a18 100644 --- a/packages/jasmine-parameterized/CHANGELOG.json +++ b/packages/jasmine-parameterized/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@ni/jasmine-parameterized", "entries": [ + { + "date": "Tue, 12 Mar 2024 21:01:53 GMT", + "version": "0.2.3", + "tag": "@ni/jasmine-parameterized_v0.2.3", + "comments": { + "patch": [ + { + "author": "7282195+m-akinc@users.noreply.github.com", + "package": "@ni/jasmine-parameterized", + "commit": "bc825e1b057eafd8bc005d11e9a224aa9aee9619", + "comment": "Update typescript to 4.9.5" + } + ] + } + }, { "date": "Wed, 06 Mar 2024 17:56:10 GMT", "version": "0.2.2", diff --git a/packages/jasmine-parameterized/CHANGELOG.md b/packages/jasmine-parameterized/CHANGELOG.md index aed76ed891..27207158d3 100644 --- a/packages/jasmine-parameterized/CHANGELOG.md +++ b/packages/jasmine-parameterized/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @ni/jasmine-parameterized -This log was last generated on Wed, 21 Feb 2024 19:48:20 GMT and should not be manually modified. +This log was last generated on Tue, 12 Mar 2024 21:01:53 GMT and should not be manually modified. +## 0.2.3 + +Tue, 12 Mar 2024 21:01:53 GMT + +### Patches + +- Update typescript to 4.9.5 ([ni/nimble@bc825e1](https://github.com/ni/nimble/commit/bc825e1b057eafd8bc005d11e9a224aa9aee9619)) + ## 0.2.2 Wed, 21 Feb 2024 19:48:20 GMT diff --git a/packages/jasmine-parameterized/package.json b/packages/jasmine-parameterized/package.json index 77b981e9d0..5683dbbc83 100644 --- a/packages/jasmine-parameterized/package.json +++ b/packages/jasmine-parameterized/package.json @@ -1,6 +1,6 @@ { "name": "@ni/jasmine-parameterized", - "version": "0.2.2", + "version": "0.2.3", "description": "A utility to write parameterized jasmine tests", "keywords": [ "jasmine", @@ -52,6 +52,6 @@ "karma-jasmine-html-reporter": "^2.0.0", "karma-spec-reporter": "^0.0.36", "playwright": "1.40.0", - "typescript": "~4.8.2" + "typescript": "~4.9.5" } } diff --git a/packages/nimble-blazor/CHANGELOG.json b/packages/nimble-blazor/CHANGELOG.json index 58bd153016..8beb296a92 100644 --- a/packages/nimble-blazor/CHANGELOG.json +++ b/packages/nimble-blazor/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@ni/nimble-blazor", "entries": [ + { + "date": "Wed, 13 Mar 2024 23:07:24 GMT", + "version": "14.4.0", + "tag": "@ni/nimble-blazor_v14.4.0", + "comments": { + "minor": [ + { + "author": "jattasNI@users.noreply.github.com", + "package": "@ni/nimble-blazor", + "commit": "518c229b964d2c4a2e77210692f5d367f2937ff4", + "comment": "Expose additional label provider properties" + } + ] + } + }, { "date": "Wed, 06 Mar 2024 17:56:10 GMT", "version": "14.3.17", diff --git a/packages/nimble-blazor/CHANGELOG.md b/packages/nimble-blazor/CHANGELOG.md index 182cfce82c..fce7a6df3e 100644 --- a/packages/nimble-blazor/CHANGELOG.md +++ b/packages/nimble-blazor/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @ni/nimble-blazor -This log was last generated on Tue, 05 Mar 2024 21:15:27 GMT and should not be manually modified. +This log was last generated on Wed, 13 Mar 2024 23:07:24 GMT and should not be manually modified. +## 14.4.0 + +Wed, 13 Mar 2024 23:07:24 GMT + +### Minor changes + +- Expose additional label provider properties ([ni/nimble@518c229](https://github.com/ni/nimble/commit/518c229b964d2c4a2e77210692f5d367f2937ff4)) + ## 14.3.15 Tue, 05 Mar 2024 21:15:27 GMT diff --git a/packages/nimble-blazor/NimbleBlazor/Components/NimbleLabelProviderCore.razor b/packages/nimble-blazor/NimbleBlazor/Components/NimbleLabelProviderCore.razor index 4d7f99c505..f5a2274346 100644 --- a/packages/nimble-blazor/NimbleBlazor/Components/NimbleLabelProviderCore.razor +++ b/packages/nimble-blazor/NimbleBlazor/Components/NimbleLabelProviderCore.razor @@ -3,5 +3,10 @@ popup-dismiss="@PopupDismiss" numeric-decrement="@NumericDecrement" numeric-increment="@NumericIncrement" + popup-icon-error="@PopupIconError" + popup-icon-warning="@PopupIconWarning" + popup-icon-information="@PopupIconInformation" + filter-search="@FilterSearch" + filter-no-results="@FilterNoResults" @attributes="AdditionalAttributes"> diff --git a/packages/nimble-blazor/NimbleBlazor/Components/NimbleLabelProviderCore.razor.cs b/packages/nimble-blazor/NimbleBlazor/Components/NimbleLabelProviderCore.razor.cs index e2e23abe27..eeb080d23a 100644 --- a/packages/nimble-blazor/NimbleBlazor/Components/NimbleLabelProviderCore.razor.cs +++ b/packages/nimble-blazor/NimbleBlazor/Components/NimbleLabelProviderCore.razor.cs @@ -16,6 +16,21 @@ public partial class NimbleLabelProviderCore : ComponentBase [Parameter] public string? NumericDecrement { get; set; } + [Parameter] + public string? PopupIconError { get; set; } + + [Parameter] + public string? PopupIconWarning { get; set; } + + [Parameter] + public string? PopupIconInformation { get; set; } + + [Parameter] + public string? FilterSearch { get; set; } + + [Parameter] + public string? FilterNoResults { get; set; } + /// /// Gets or sets a collection of additional attributes that will be applied to the created element. /// diff --git a/packages/nimble-blazor/NimbleBlazor/Components/NimbleLabelProviderTable.razor b/packages/nimble-blazor/NimbleBlazor/Components/NimbleLabelProviderTable.razor index 50351dc8bc..a71b904408 100644 --- a/packages/nimble-blazor/NimbleBlazor/Components/NimbleLabelProviderTable.razor +++ b/packages/nimble-blazor/NimbleBlazor/Components/NimbleLabelProviderTable.razor @@ -2,6 +2,8 @@ { + let styleElement = document.querySelector('.nimble-background-workaround'); + if (!styleElement) { + styleElement = document.createElement('style'); + styleElement.classList.add('nimble-background-workaround'); + document.head.append(styleElement); + } + styleElement.innerHTML = style; +}; + +// Storybook background plugin does not support mdx +// Workaround based on: https://github.com/storybookjs/storybook/issues/13323#issuecomment-876296801 +export default { + decorators: [ + (story, context) => { + const defaultBackgroundColorKey = context?.parameters?.backgrounds?.default; + const defaultBackgroundColor = context?.parameters?.backgrounds?.values?.find(v => v.name === defaultBackgroundColorKey)?.value; + const currentBackgroundColor = context?.globals?.backgrounds?.value ?? defaultBackgroundColor; + const style = `.docs-story { + background-color: ${currentBackgroundColor} + }`; + createOrUpdateBackgroundWorkaround(style); + return story(); + } + ] +}; + // Storybook's default serialization of events includes the serialized event target. This can // be quite large, such as in a table with a lot of records. Therefore, the serialization depth // should be limited to avoid poor performance. diff --git a/packages/nimble-components/.storybook/transformSource.js b/packages/nimble-components/.storybook/transformSource.js index d5ac03dec0..19c8c685f1 100644 --- a/packages/nimble-components/.storybook/transformSource.js +++ b/packages/nimble-components/.storybook/transformSource.js @@ -1,5 +1,4 @@ -import prettier from 'prettier/standalone'; -import parserHTML from 'prettier/plugins/html'; +const beautifyHTML = require('js-beautify').html; const createFragmentFromHTML = html => { const template = document.createElement('template'); @@ -51,7 +50,10 @@ const removeClassAttributes = node => { } // Assume that all class attributes added to nimble elements were added by FAST // and are not part of the control api - if (node instanceof HTMLElement && node.tagName.toLowerCase().startsWith('nimble-')) { + if ( + node instanceof HTMLElement + && node.tagName.toLowerCase().startsWith('nimble-') + ) { node.removeAttribute('class'); } }; @@ -61,8 +63,7 @@ const removeBlankLines = html => html .filter(line => line.trim() !== '') .join('\n'); -const removeEmptyAttributes = html => html - .replaceAll('=""', ''); +const removeEmptyAttributes = html => html.replaceAll('=""', ''); // A custom source transformer. See: // https://github.com/storybookjs/storybook/blob/next/addons/docs/docs/recipes.md#customizing-source-snippets @@ -78,11 +79,8 @@ export const transformSource = source => { const trimmedHTML = removeBlankLines(html); const emptyAttributesRemovedHTML = removeEmptyAttributes(trimmedHTML); - const formmattedHTML = prettier.format(emptyAttributesRemovedHTML, { - parser: 'html', - plugins: [parserHTML], - htmlWhitespaceSensitivity: 'ignore', - tabWidth: 4 + const formmattedHTML = beautifyHTML(emptyAttributesRemovedHTML, { + wrap_attributes: 'force-expand-multiline' }); return formmattedHTML; }; diff --git a/packages/nimble-components/CHANGELOG.json b/packages/nimble-components/CHANGELOG.json index ca97d42e76..81794ef2c8 100644 --- a/packages/nimble-components/CHANGELOG.json +++ b/packages/nimble-components/CHANGELOG.json @@ -1,6 +1,138 @@ { "name": "@ni/nimble-components", "entries": [ + { + "date": "Thu, 14 Mar 2024 14:48:16 GMT", + "version": "22.0.1", + "tag": "@ni/nimble-components_v22.0.1", + "comments": { + "patch": [ + { + "author": "20542556+mollykreis@users.noreply.github.com", + "package": "@ni/nimble-components", + "commit": "a1eebb02b4fb66ca77e0e32bb52497b2da1cb4cf", + "comment": "Fix bug where an anchor element's link couldn't be dragged correctly" + } + ] + } + }, + { + "date": "Wed, 13 Mar 2024 23:56:16 GMT", + "version": "22.0.0", + "tag": "@ni/nimble-components_v22.0.0", + "comments": { + "none": [ + { + "author": "rajsite@users.noreply.github.com", + "package": "@ni/nimble-components", + "commit": "2a9abefcd6160716070fd4f3bbca097919112d23", + "comment": "Build warnings for apache-arrow were cleaned up" + } + ] + } + }, + { + "date": "Wed, 13 Mar 2024 23:07:24 GMT", + "version": "22.0.0", + "tag": "@ni/nimble-components_v22.0.0", + "comments": { + "major": [ + { + "author": "jattasNI@users.noreply.github.com", + "package": "@ni/nimble-components", + "commit": "518c229b964d2c4a2e77210692f5d367f2937ff4", + "comment": "Rename icon labels to follow naming convention" + } + ] + } + }, + { + "date": "Wed, 13 Mar 2024 22:25:27 GMT", + "version": "21.10.2", + "tag": "@ni/nimble-components_v21.10.2", + "comments": { + "none": [ + { + "author": "7282195+m-akinc@users.noreply.github.com", + "package": "@ni/nimble-components", + "commit": "52783423015bc7b1cfd3718646923d36b1f9d32c", + "comment": "Fix missing element names in some Storybook docs" + } + ] + } + }, + { + "date": "Tue, 12 Mar 2024 22:17:11 GMT", + "version": "21.10.2", + "tag": "@ni/nimble-components_v21.10.2", + "comments": { + "patch": [ + { + "author": "26874831+atmgrifter00@users.noreply.github.com", + "package": "@ni/nimble-components", + "commit": "19643f4e13056e414aedd6b8ef89beb7ad1e95ab", + "comment": "Adding DropdownOwner interface allowing Select to register options during connectedCallback." + } + ] + } + }, + { + "date": "Tue, 12 Mar 2024 21:01:54 GMT", + "version": "21.10.1", + "tag": "@ni/nimble-components_v21.10.1", + "comments": { + "patch": [ + { + "author": "7282195+m-akinc@users.noreply.github.com", + "package": "@ni/nimble-components", + "commit": "bc825e1b057eafd8bc005d11e9a224aa9aee9619", + "comment": "Update typescript to 4.9.5" + }, + { + "author": "beachball", + "package": "@ni/nimble-components", + "comment": "Bump @ni/nimble-tokens to v6.12.1", + "commit": "not available" + }, + { + "author": "beachball", + "package": "@ni/nimble-components", + "comment": "Bump @ni/jasmine-parameterized to v0.2.3", + "commit": "not available" + } + ] + } + }, + { + "date": "Tue, 12 Mar 2024 20:17:37 GMT", + "version": "21.10.0", + "tag": "@ni/nimble-components_v21.10.0", + "comments": { + "none": [ + { + "author": "rajsite@users.noreply.github.com", + "package": "@ni/nimble-components", + "commit": "e5972ee4e47f02a4224311d696da2d15573dbab3", + "comment": "Fix sb mdx backgrounds" + } + ] + } + }, + { + "date": "Tue, 12 Mar 2024 16:59:31 GMT", + "version": "21.10.0", + "tag": "@ni/nimble-components_v21.10.0", + "comments": { + "none": [ + { + "author": "1458528+fredvisser@users.noreply.github.com", + "package": "@ni/nimble-components", + "commit": "d8aa87d33c0066f7f3f73348288222c30a2a8cbf", + "comment": "Fix Storybook source preview" + } + ] + } + }, { "date": "Thu, 07 Mar 2024 22:17:07 GMT", "version": "21.10.0", diff --git a/packages/nimble-components/CHANGELOG.md b/packages/nimble-components/CHANGELOG.md index 7909968930..2664591847 100644 --- a/packages/nimble-components/CHANGELOG.md +++ b/packages/nimble-components/CHANGELOG.md @@ -1,9 +1,43 @@ # Change Log - @ni/nimble-components -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 Thu, 14 Mar 2024 14:48:16 GMT and should not be manually modified. +## 22.0.1 + +Thu, 14 Mar 2024 14:48:16 GMT + +### Patches + +- Fix bug where an anchor element's link couldn't be dragged correctly ([ni/nimble@a1eebb0](https://github.com/ni/nimble/commit/a1eebb02b4fb66ca77e0e32bb52497b2da1cb4cf)) + +## 22.0.0 + +Wed, 13 Mar 2024 23:07:24 GMT + +### Major changes + +- Rename icon labels to follow naming convention ([ni/nimble@518c229](https://github.com/ni/nimble/commit/518c229b964d2c4a2e77210692f5d367f2937ff4)) + +## 21.10.2 + +Tue, 12 Mar 2024 22:17:11 GMT + +### Patches + +- Adding DropdownOwner interface allowing Select to register options during connectedCallback. ([ni/nimble@19643f4](https://github.com/ni/nimble/commit/19643f4e13056e414aedd6b8ef89beb7ad1e95ab)) + +## 21.10.1 + +Tue, 12 Mar 2024 21:01:54 GMT + +### Patches + +- Update typescript to 4.9.5 ([ni/nimble@bc825e1](https://github.com/ni/nimble/commit/bc825e1b057eafd8bc005d11e9a224aa9aee9619)) +- Bump @ni/nimble-tokens to v6.12.1 +- Bump @ni/jasmine-parameterized to v0.2.3 + ## 21.10.0 Thu, 07 Mar 2024 21:20:52 GMT diff --git a/packages/nimble-components/package.json b/packages/nimble-components/package.json index 381faa6616..20bf058f61 100644 --- a/packages/nimble-components/package.json +++ b/packages/nimble-components/package.json @@ -1,6 +1,6 @@ { "name": "@ni/nimble-components", - "version": "21.10.0", + "version": "22.0.1", "description": "Styled web components for the NI Nimble Design System", "scripts": { "build": "npm run generate-icons && npm run generate-workers && npm run build-components && npm run bundle-components && npm run generate-scss && npm run build-storybook", @@ -68,7 +68,7 @@ "@microsoft/fast-element": "^1.12.0", "@microsoft/fast-foundation": "2.49.4", "@microsoft/fast-web-utilities": "^6.0.0", - "@ni/nimble-tokens": "^6.12.0", + "@ni/nimble-tokens": "^6.12.1", "@tanstack/table-core": "^8.10.7", "@tanstack/virtual-core": "^3.0.0-beta.68", "@tiptap/core": "^2.2.2", @@ -111,7 +111,7 @@ "@microsoft/fast-react-wrapper": "0.3.22", "@ni/eslint-config-javascript": "^4.2.0", "@ni/eslint-config-typescript": "^4.2.0", - "@ni/jasmine-parameterized": "^0.2.2", + "@ni/jasmine-parameterized": "^0.2.3", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.1", @@ -139,6 +139,7 @@ "eslint-plugin-storybook": "^0.8.0", "html-webpack-plugin": "^5.3.1", "jasmine-core": "^5.1.2", + "js-beautify": "^1.15.1", "karma": "^6.3.0", "karma-chrome-launcher": "^3.1.0", "karma-firefox-launcher": "^2.1.0", @@ -151,7 +152,6 @@ "karma-webkit-launcher": "^2.1.0", "karma-webpack": "^5.0.0", "playwright": "1.40.0", - "prettier": "^3.2.5", "prettier-eslint": "^16.3.0", "prettier-eslint-cli": "^8.0.1", "remark-gfm": "^3.0.1", @@ -161,7 +161,7 @@ "source-map-loader": "^5.0.0", "storybook": "^7.6.13", "ts-loader": "^9.2.5", - "typescript": "~4.8.2", + "typescript": "~4.9.5", "webpack": "^5.75.0", "webpack-cli": "^5.0.1", "webpack-dev-middleware": "^7.0.0" diff --git a/packages/nimble-components/rollup.config.js b/packages/nimble-components/rollup.config.js index 1ae9096cdf..1206e22e22 100644 --- a/packages/nimble-components/rollup.config.js +++ b/packages/nimble-components/rollup.config.js @@ -16,17 +16,21 @@ const umdProductionPlugin = () => replace({ preventAssignment: true }); -// d3 has circular dependencies it won't remove: -// https://github.com/d3/d3-selection/issues/168 -// So ignore just d3's circular dependencies following the pattern from: -// https://github.com/rollup/rollup/issues/1089#issuecomment-635564942 -// Updated to use current onwarn api: +// Custom onwarn handler for ignoring specific warnings: // https://rollupjs.org/configuration-options/#onwarn const onwarn = (warning, defaultHandler) => { const ignoredWarnings = [ + // d3 has circular dependencies it won't remove: + // See https://github.com/d3/d3-selection/issues/168 { code: 'CIRCULAR_DEPENDENCY', file: 'node_modules/d3-' + }, + // apache-arrow has circular dependencies: + // See https://github.com/apache/arrow/issues/40516 + { + code: 'CIRCULAR_DEPENDENCY', + file: 'node_modules/apache-arrow' } ]; diff --git a/packages/nimble-components/src/anchor-button/styles.ts b/packages/nimble-components/src/anchor-button/styles.ts index f57265ea8d..1393bd4360 100644 --- a/packages/nimble-components/src/anchor-button/styles.ts +++ b/packages/nimble-components/src/anchor-button/styles.ts @@ -11,4 +11,16 @@ export const styles = css` .control { text-decoration: none; } + + [part='start'] { + pointer-events: none; + } + + .content { + pointer-events: none; + } + + [part='end'] { + pointer-events: none; + } `; diff --git a/packages/nimble-components/src/anchor-menu-item/styles.ts b/packages/nimble-components/src/anchor-menu-item/styles.ts index d108f5dad6..fb5e5d8fe2 100644 --- a/packages/nimble-components/src/anchor-menu-item/styles.ts +++ b/packages/nimble-components/src/anchor-menu-item/styles.ts @@ -69,6 +69,7 @@ export const styles = css` [part='start'] { display: contents; + pointer-events: none; } slot[name='start']::slotted(*) { @@ -81,6 +82,10 @@ export const styles = css` grid-column: 1; } + .content { + pointer-events: none; + } + :host(.indent-1) .content { grid-column: 2; } diff --git a/packages/nimble-components/src/anchor-tab/styles.ts b/packages/nimble-components/src/anchor-tab/styles.ts index 467ce518dc..979e65b672 100644 --- a/packages/nimble-components/src/anchor-tab/styles.ts +++ b/packages/nimble-components/src/anchor-tab/styles.ts @@ -52,6 +52,7 @@ export const styles = css` } slot:not([name]) { + pointer-events: none; display: block; padding: ${mediumPadding} ${standardPadding} calc(${mediumPadding} - ${borderWidth}); diff --git a/packages/nimble-components/src/anchor-tabs/tests/anchor-tabs.mdx b/packages/nimble-components/src/anchor-tabs/tests/anchor-tabs.mdx index f176c47ade..89ff9ce3c8 100644 --- a/packages/nimble-components/src/anchor-tabs/tests/anchor-tabs.mdx +++ b/packages/nimble-components/src/anchor-tabs/tests/anchor-tabs.mdx @@ -29,7 +29,7 @@ tab panels hosted on the same page. ## Angular Usage In an Angular application, it is common to integrate with the router by setting `nimbleRouterLink` (rather than `href`) -on each element. In those cases, it is recommended to also set `replaceUrl="true"` so that switching +on each element. In those cases, it is recommended to also set `replaceUrl="true"` so that switching between tabs does not add to the browser history. {/* ## Accessibility */} diff --git a/packages/nimble-components/src/anchor-tree-item/styles.ts b/packages/nimble-components/src/anchor-tree-item/styles.ts index bf91df7fcc..f1b66ff96c 100644 --- a/packages/nimble-components/src/anchor-tree-item/styles.ts +++ b/packages/nimble-components/src/anchor-tree-item/styles.ts @@ -100,6 +100,7 @@ export const styles = css` } [part="start"] { width: ${iconSize}; + pointer-events: none; } ${/* the start class is applied when the corresponding slot is filled */ ''} @@ -115,6 +116,10 @@ export const styles = css` height: ${iconSize}; } + .content { + pointer-events: none; + } + [part='end'] { display: none; } diff --git a/packages/nimble-components/src/anchor/styles.ts b/packages/nimble-components/src/anchor/styles.ts index 9d1f1a9c0e..3e5ed018a9 100644 --- a/packages/nimble-components/src/anchor/styles.ts +++ b/packages/nimble-components/src/anchor/styles.ts @@ -93,6 +93,10 @@ export const styles = css` color: ${linkProminentDisabledFontColor}; } + .content { + pointer-events: none; + } + [part='end'] { display: none; } diff --git a/packages/nimble-components/src/banner/template.ts b/packages/nimble-components/src/banner/template.ts index 889b0025d8..c2568336d9 100644 --- a/packages/nimble-components/src/banner/template.ts +++ b/packages/nimble-components/src/banner/template.ts @@ -7,10 +7,10 @@ import { iconTriangleFilledTag } from '../icons/triangle-filled'; import { iconXmarkTag } from '../icons/xmark'; import { BannerSeverity } from './types'; import { - errorIconLabel, - informationIconLabel, + popupIconErrorLabel, + popupIconInformationLabel, popupDismissLabel, - warningIconLabel + popupIconWarningLabel } from '../label-provider/core/label-tokens'; // prettier-ignore @@ -40,13 +40,13 @@ export const template = html` >
${when(x => x.severity === BannerSeverity.error, html` - <${iconExclamationMarkTag} role="img" aria-label="${x => errorIconLabel.getValueFor(x)}"> + <${iconExclamationMarkTag} role="img" aria-label="${x => popupIconErrorLabel.getValueFor(x)}"> `)} ${when(x => x.severity === BannerSeverity.warning, html` - <${iconTriangleFilledTag} role="img" aria-label="${x => warningIconLabel.getValueFor(x)}"> + <${iconTriangleFilledTag} role="img" aria-label="${x => popupIconWarningLabel.getValueFor(x)}"> `)} ${when(x => x.severity === BannerSeverity.information, html` - <${iconInfoTag} role="img" aria-label="${x => informationIconLabel.getValueFor(x)}"> + <${iconInfoTag} role="img" aria-label="${x => popupIconInformationLabel.getValueFor(x)}"> `)}
diff --git a/packages/nimble-components/src/breadcrumb-item/styles.ts b/packages/nimble-components/src/breadcrumb-item/styles.ts index f65b78859d..091265edf3 100644 --- a/packages/nimble-components/src/breadcrumb-item/styles.ts +++ b/packages/nimble-components/src/breadcrumb-item/styles.ts @@ -61,6 +61,10 @@ export const styles = css` display: none; } + .content { + pointer-events: none; + } + [part='end'] { display: none; } diff --git a/packages/nimble-components/src/card/tests/card.mdx b/packages/nimble-components/src/card/tests/card.mdx index 4113b8eb7d..a5c8085597 100644 --- a/packages/nimble-components/src/card/tests/card.mdx +++ b/packages/nimble-components/src/card/tests/card.mdx @@ -6,8 +6,8 @@ import * as cardStories from './card.stories'; -The <Tag of={cardTag} /> is a container that is designed to contain arbitrary content that is specified by a client -application. The <Tag of={cardTag} /> is intended for grouping related content. +The <Tag name={cardTag} /> is a container that is designed to contain arbitrary content that is specified by a client +application. The <Tag name={cardTag} /> is intended for grouping related content. <Canvas of={cardStories.card} /> <Controls of={cardStories.card} /> diff --git a/packages/nimble-components/src/combobox/index.ts b/packages/nimble-components/src/combobox/index.ts index e3da226ded..4f607c5439 100644 --- a/packages/nimble-components/src/combobox/index.ts +++ b/packages/nimble-components/src/combobox/index.ts @@ -17,10 +17,14 @@ import { iconExclamationMarkTag } from '../icons/exclamation-mark'; import { styles } from './styles'; import type { ErrorPattern } from '../patterns/error/types'; -import type { DropdownPattern } from '../patterns/dropdown/types'; +import type { + DropdownPattern, + ListOptionOwner +} from '../patterns/dropdown/types'; import { DropdownAppearance } from '../patterns/dropdown/types'; import type { AnchoredRegion } from '../anchored-region'; import { template } from './template'; +import type { ListOption } from '../list-option'; declare global { interface HTMLElementTagNameMap { @@ -33,7 +37,7 @@ declare global { */ export class Combobox extends FoundationCombobox - implements DropdownPattern, ErrorPattern { + implements DropdownPattern, ErrorPattern, ListOptionOwner { @attr public appearance: DropdownAppearance = DropdownAppearance.underline; @@ -207,6 +211,23 @@ export class Combobox return returnValue; } + /** + * @internal + */ + public registerOption(option: ListOption): void { + if (this.options.includes(option)) { + return; + } + + // Adding an option to the end, ultimately, isn't the correct + // thing to do, as this will mean the option's index in the options, + // at least temporarily, does not match the DOM order. However, it + // is expected that a successive run of `slottedOptionsChanged` will + // correct this order issue. See 'https://github.com/ni/nimble/issues/1915' + // for more info. + this.options.push(option); + } + protected override focusAndScrollOptionIntoView(): void { if (this.open) { super.focusAndScrollOptionIntoView(); diff --git a/packages/nimble-components/src/combobox/tests/combobox.mdx b/packages/nimble-components/src/combobox/tests/combobox.mdx index 254ea40c8e..0870896932 100644 --- a/packages/nimble-components/src/combobox/tests/combobox.mdx +++ b/packages/nimble-components/src/combobox/tests/combobox.mdx @@ -19,11 +19,11 @@ import { listOptionTag } from '../../list-option/'; ### Native element and Blazor -The value of the combobox comes from the text content of the selected autocomplete <Tag of={listOptionTag} />, or, if no matching autocomplete is found, the value is the user-entered text. +The value of the combobox comes from the text content of the selected autocomplete <Tag name={listOptionTag} />, or, if no matching autocomplete is found, the value is the user-entered text. ### Angular -In Angular, an autocomplete <Tag of={listOptionTag} /> can be created with an `ngValue`. In that case the `ngModel` of the combobox will be the `ngValue` of the matched autocomplete option. +In Angular, an autocomplete <Tag name={listOptionTag} /> can be created with an `ngValue`. In that case the `ngModel` of the combobox will be the `ngValue` of the matched autocomplete option. If no matching autocomplete option is found, the `ngModel` of the combobox will be set to `OPTION_NOT_FOUND`, and if the application needs to access the user-entered text, that should be done through the `value` property on the `NimbleComboboxDirective`. diff --git a/packages/nimble-components/src/combobox/tests/combobox.spec.ts b/packages/nimble-components/src/combobox/tests/combobox.spec.ts index 242d147c33..7b282bd515 100644 --- a/packages/nimble-components/src/combobox/tests/combobox.spec.ts +++ b/packages/nimble-components/src/combobox/tests/combobox.spec.ts @@ -2,7 +2,6 @@ import { html, repeat } from '@microsoft/fast-element'; import { keyArrowDown, keyEnter } from '@microsoft/fast-web-utilities'; import { fixture, Fixture } from '../../utilities/tests/fixture'; import { Combobox, comboboxTag } from '..'; -import { listOptionTag } from '../../list-option'; import { ComboboxAutocomplete } from '../types'; import { waitForUpdatesAsync } from '../../testing/async-helpers'; import { @@ -10,6 +9,7 @@ import { waitAnimationFrame } from '../../utilities/tests/component'; import { checkFullyInViewport } from '../../utilities/tests/intersection-observer'; +import { ListOption, listOptionTag } from '../../list-option'; async function setup( position?: string, @@ -127,6 +127,33 @@ describe('Combobox', () => { await disconnect(); }); + it('option added directly to DOM synchronously registers with Combobox', async () => { + const { element, connect, disconnect } = await setup(); + await connect(); + element.selectedIndex = 0; + await waitForUpdatesAsync(); + const newOption = new ListOption('foo', 'foo'); + const registerOptionSpy = spyOn( + element, + 'registerOption' + ).and.callThrough(); + registerOptionSpy.calls.reset(); + element.insertBefore(newOption, element.options[0]!); + + expect(registerOptionSpy.calls.count()).toBe(1); + expect(element.options).toContain(newOption); + + // While the option is registered synchronously as shown above, + // properties like selectedIndex will only be correct asynchronously + // See https://github.com/ni/nimble/issues/1915 + expect(element.selectedIndex).toBe(0); + await waitForUpdatesAsync(); + // This assertion shows that after 'slottedOptionsChanged' runs, the + // 'selectedIndex' state has been corrected to expected DOM order. + expect(element.selectedIndex).toBe(1); + await disconnect(); + }); + const ariaTestData: { attrName: string, propSetter: (x: Combobox, value: string) => void diff --git a/packages/nimble-components/src/label-provider/core/index.ts b/packages/nimble-components/src/label-provider/core/index.ts index f7c901dc12..b0a872a5e7 100644 --- a/packages/nimble-components/src/label-provider/core/index.ts +++ b/packages/nimble-components/src/label-provider/core/index.ts @@ -5,9 +5,9 @@ import { popupDismissLabel, numericDecrementLabel, numericIncrementLabel, - errorIconLabel, - warningIconLabel, - informationIconLabel, + popupIconErrorLabel, + popupIconWarningLabel, + popupIconInformationLabel, filterSearchLabel, filterNoResultsLabel } from './label-tokens'; @@ -22,9 +22,9 @@ const supportedLabels = { popupDismiss: popupDismissLabel, numericDecrement: numericDecrementLabel, numericIncrement: numericIncrementLabel, - errorIcon: errorIconLabel, - warningIcon: warningIconLabel, - informationIcon: informationIconLabel, + popupIconError: popupIconErrorLabel, + popupIconWarning: popupIconWarningLabel, + popupIconInformation: popupIconInformationLabel, filterSearch: filterSearchLabel, filterNoResults: filterNoResultsLabel } as const; @@ -44,14 +44,14 @@ export class LabelProviderCore @attr({ attribute: 'numeric-increment' }) public numericIncrement: string | undefined; - @attr({ attribute: 'error-icon' }) - public errorIcon: string | undefined; + @attr({ attribute: 'popup-icon-error' }) + public popupIconError: string | undefined; - @attr({ attribute: 'warning-icon' }) - public warningIcon: string | undefined; + @attr({ attribute: 'popup-icon-warning' }) + public popupIconWarning: string | undefined; - @attr({ attribute: 'information-icon' }) - public informationIcon: string | undefined; + @attr({ attribute: 'popup-icon-information' }) + public popupIconInformation: string | undefined; @attr({ attribute: 'filter-search' }) public filterSearch: string | undefined; diff --git a/packages/nimble-components/src/label-provider/core/label-token-defaults.ts b/packages/nimble-components/src/label-provider/core/label-token-defaults.ts index 91ed6e5993..b555c28f27 100644 --- a/packages/nimble-components/src/label-provider/core/label-token-defaults.ts +++ b/packages/nimble-components/src/label-provider/core/label-token-defaults.ts @@ -6,9 +6,9 @@ export const coreLabelDefaults: { readonly [key in TokenName]: string } = { popupDismissLabel: 'Close', numericIncrementLabel: 'Increment', numericDecrementLabel: 'Decrement', - errorIconLabel: 'Error', - warningIconLabel: 'Warning', - informationIconLabel: 'Information', + popupIconErrorLabel: 'Error', + popupIconWarningLabel: 'Warning', + popupIconInformationLabel: 'Information', filterSearchLabel: 'Search', filterNoResultsLabel: 'No items found' }; diff --git a/packages/nimble-components/src/label-provider/core/label-tokens.ts b/packages/nimble-components/src/label-provider/core/label-tokens.ts index 2cfe2debce..2f6f518ec5 100644 --- a/packages/nimble-components/src/label-provider/core/label-tokens.ts +++ b/packages/nimble-components/src/label-provider/core/label-tokens.ts @@ -16,20 +16,20 @@ export const numericIncrementLabel = DesignToken.create<string>({ cssCustomPropertyName: null }).withDefault(coreLabelDefaults.numericIncrementLabel); -export const errorIconLabel = DesignToken.create<string>({ - name: 'error-icon-label', +export const popupIconErrorLabel = DesignToken.create<string>({ + name: 'popup-icon-error-label', cssCustomPropertyName: null -}).withDefault(coreLabelDefaults.errorIconLabel); +}).withDefault(coreLabelDefaults.popupIconErrorLabel); -export const warningIconLabel = DesignToken.create<string>({ - name: 'warning-icon-label', +export const popupIconWarningLabel = DesignToken.create<string>({ + name: 'popup-icon-warning-label', cssCustomPropertyName: null -}).withDefault(coreLabelDefaults.warningIconLabel); +}).withDefault(coreLabelDefaults.popupIconWarningLabel); -export const informationIconLabel = DesignToken.create<string>({ - name: 'information-icon-label', +export const popupIconInformationLabel = DesignToken.create<string>({ + name: 'popup-icon-information-label', cssCustomPropertyName: null -}).withDefault(coreLabelDefaults.informationIconLabel); +}).withDefault(coreLabelDefaults.popupIconInformationLabel); export const filterSearchLabel = DesignToken.create<string>({ name: 'filter-search-label', diff --git a/packages/nimble-components/src/list-option/index.ts b/packages/nimble-components/src/list-option/index.ts index 724e564514..6f60d343e2 100644 --- a/packages/nimble-components/src/list-option/index.ts +++ b/packages/nimble-components/src/list-option/index.ts @@ -5,6 +5,7 @@ import { import { observable, attr } from '@microsoft/fast-element'; import { styles } from './styles'; import { template } from './template'; +import type { ListOptionOwner } from '../patterns/dropdown/types'; declare global { interface HTMLElementTagNameMap { @@ -51,6 +52,23 @@ export class ListOption extends FoundationListboxOption { .map(node => node.textContent?.trim()) .join(' '); } + + public override connectedCallback(): void { + super.connectedCallback(); + if (this.isListOptionOwner(this.parentElement)) { + this.parentElement.registerOption(this); + } + } + + private isListOptionOwner( + parent: HTMLElement | null + ): parent is ListOptionOwner { + if (!parent) { + return false; + } + + return typeof (parent as ListOptionOwner).registerOption === 'function'; + } } const nimbleListOption = ListOption.compose({ diff --git a/packages/nimble-components/src/patterns/dropdown/types.ts b/packages/nimble-components/src/patterns/dropdown/types.ts index 7cebcb4e7d..3e1178b895 100644 --- a/packages/nimble-components/src/patterns/dropdown/types.ts +++ b/packages/nimble-components/src/patterns/dropdown/types.ts @@ -1,3 +1,4 @@ +import type { ListOption } from '../../list-option'; import type { ErrorPattern } from '../error/types'; /** @@ -22,3 +23,17 @@ export const DropdownAppearance = { } as const; export type DropdownAppearance = (typeof DropdownAppearance)[keyof typeof DropdownAppearance]; + +/** + * @internal + * + * This interface is used to register options with their parent once their + * 'connectedCallback' method is run. This allows for the "owner", like the + * Select, to have its value set to that newly registered option earlier than it + * might otherwise in certain situations. One such scenario is in an Angular + * reactive form, where the form value is set to an option immediately after + * dynamically adding it. + */ +export interface ListOptionOwner extends HTMLElement { + registerOption: (option: ListOption) => void; +} diff --git a/packages/nimble-components/src/select/index.ts b/packages/nimble-components/src/select/index.ts index 5370b1f06f..5cd9a29f4b 100644 --- a/packages/nimble-components/src/select/index.ts +++ b/packages/nimble-components/src/select/index.ts @@ -30,7 +30,10 @@ import { } from '@microsoft/fast-web-utilities'; import { arrowExpanderDown16X16 } from '@ni/nimble-tokens/dist/icons/js'; import { styles } from './styles'; -import { DropdownAppearance } from '../patterns/dropdown/types'; +import { + DropdownAppearance, + ListOptionOwner +} from '../patterns/dropdown/types'; import { errorTextTemplate } from '../patterns/error/template'; import type { ErrorPattern } from '../patterns/error/types'; import { iconExclamationMarkTag } from '../icons/exclamation-mark'; @@ -57,7 +60,9 @@ const isNimbleListOption = (el: Element): el is ListOption => { /** * A nimble-styled HTML select. */ -export class Select extends FormAssociatedSelect implements ErrorPattern { +export class Select + extends FormAssociatedSelect + implements ErrorPattern, ListOptionOwner { @attr public appearance: DropdownAppearance = DropdownAppearance.underline; @@ -668,6 +673,23 @@ export class Select extends FormAssociatedSelect implements ErrorPattern { } } + /** + * @internal + */ + public registerOption(option: ListOption): void { + if (this.options.includes(option)) { + return; + } + + // Adding an option to the end, ultimately, isn't the correct + // thing to do, as this will mean the option's index in the options, + // at least temporarily, does not match the DOM order. However, it + // is expected that a successive run of `slottedOptionsChanged` will + // correct this order issue. See 'https://github.com/ni/nimble/issues/1915' + // for more info. + this.options.push(option); + } + // Prevents parent classes from resetting selectedIndex to a positive // value while filtering, which can result in a disabled option being // selected. diff --git a/packages/nimble-components/src/select/testing/select.pageobject.ts b/packages/nimble-components/src/select/testing/select.pageobject.ts index c8a5d236f0..4ea4cda6db 100644 --- a/packages/nimble-components/src/select/testing/select.pageobject.ts +++ b/packages/nimble-components/src/select/testing/select.pageobject.ts @@ -23,7 +23,7 @@ export class SelectPageObject { 'Can not set filter text with filterMode set to "none".' ); } - await this.clickSelect(); + this.clickSelect(); const filterInput = this.getFilterInput(); if (filterInput) { filterInput.value = filterText; @@ -59,9 +59,8 @@ export class SelectPageObject { /** * Either opens or closes the dropdown depending on its current state */ - public async clickSelect(): Promise<void> { + public clickSelect(): void { this.selectElement.click(); - await waitForUpdatesAsync(); } public clickSelectedItem(): void { @@ -96,11 +95,9 @@ export class SelectPageObject { * Click the option with the text provided by the 'displayText' parameter. * @param value The text of the option to be selected */ - public async clickOptionWithDisplayText( - displayText: string - ): Promise<void> { + public clickOptionWithDisplayText(displayText: string): void { if (!this.selectElement.open) { - await this.clickSelect(); + this.clickSelect(); } const optionIndex = this.selectElement.options.findIndex( o => o.text === displayText diff --git a/packages/nimble-components/src/select/tests/select.spec.ts b/packages/nimble-components/src/select/tests/select.spec.ts index e754dbe5cb..5b58069bf4 100644 --- a/packages/nimble-components/src/select/tests/select.spec.ts +++ b/packages/nimble-components/src/select/tests/select.spec.ts @@ -168,7 +168,7 @@ describe('Select', () => { const { element, connect, disconnect } = await setup(); await connect(); const pageObject = new SelectPageObject(element); - await pageObject.clickSelect(); + await clickAndWaitForOpen(element); expect(pageObject.isDropdownVisible()).toBeTrue(); expect(pageObject.isFilterInputVisible()).toBeFalse(); @@ -179,7 +179,7 @@ describe('Select', () => { const { element, connect, disconnect } = await setup(); await connect(); const pageObject = new SelectPageObject(element); - await pageObject.clickSelect(); + pageObject.clickSelect(); pageObject.pressArrowDownKey(); await waitForUpdatesAsync(); expect(element.selectedIndex).toBe(1); @@ -195,7 +195,7 @@ describe('Select', () => { const { element, connect, disconnect } = await setup(); await connect(); const pageObject = new SelectPageObject(element); - await pageObject.clickSelect(); + pageObject.clickSelect(); pageObject.pressArrowDownKey(); await waitForUpdatesAsync(); @@ -207,7 +207,7 @@ describe('Select', () => { const { element, connect, disconnect } = await setup(); await connect(); const pageObject = new SelectPageObject(element); - await pageObject.clickSelect(); + pageObject.clickSelect(); pageObject.pressArrowDownKey(); await pageObject.pressSpaceKey(); expect(element.value).toBe('two'); @@ -294,6 +294,33 @@ describe('Select', () => { await disconnect(); }); + it('option added directly to DOM synchronously registers with Select', async () => { + const { element, connect, disconnect } = await setup(); + await connect(); + await waitForUpdatesAsync(); + const newOption = new ListOption('foo', 'foo'); + const registerOptionSpy = spyOn( + element, + 'registerOption' + ).and.callThrough(); + registerOptionSpy.calls.reset(); + element.insertBefore(newOption, element.options[0]!); + + expect(registerOptionSpy.calls.count()).toBe(1); + expect(element.options).toContain(newOption); + + // While the option is registered synchronously as shown above, + // properties like selectedIndex will only be correct asynchronously + // See https://github.com/ni/nimble/issues/1915 + expect(element.selectedIndex).toBe(0); + await waitForUpdatesAsync(); + expect(element.value).toBe('one'); + // This assertion shows that after 'slottedOptionsChanged' runs, the + // 'selectedIndex' state has been corrected to expected DOM order. + expect(element.selectedIndex).toBe(1); + await disconnect(); + }); + describe('with 500 options', () => { async function setup500Options(): Promise<Fixture<Select>> { // prettier-ignore @@ -482,25 +509,25 @@ describe('Select', () => { expect(element.open).toBeTrue(); }); - it('after pressing <Esc> to close dropdown, <Enter> will re-open dropdown', async () => { + it('after pressing <Esc> to close dropdown, <Enter> will re-open dropdown', () => { element.filterMode = testData.filter; - await pageObject.clickSelect(); + pageObject.clickSelect(); pageObject.pressEscapeKey(); expect(element.open).toBeFalse(); pageObject.pressEnterKey(); expect(element.open).toBeTrue(); }); - it('after closing dropdown by pressing <Esc>, activeElement is Select element', async () => { + it('after closing dropdown by pressing <Esc>, activeElement is Select element', () => { element.filterMode = testData.filter; - await pageObject.clickSelect(); + pageObject.clickSelect(); pageObject.pressEscapeKey(); expect(document.activeElement).toBe(element); }); - it('after closing dropdown by committing a value with <Enter>, activeElement is Select element', async () => { + it('after closing dropdown by committing a value with <Enter>, activeElement is Select element', () => { element.filterMode = testData.filter; - await pageObject.clickSelect(); + pageObject.clickSelect(); pageObject.pressArrowDownKey(); pageObject.pressEnterKey(); expect(document.activeElement).toBe(element); @@ -571,7 +598,7 @@ describe('Select', () => { expect(currentSelection?.text).toBe('Two'); pageObject.pressEscapeKey(); - await pageObject.clickSelect(); + pageObject.clickSelect(); currentSelection = pageObject.getSelectedOption(); expect(currentSelection?.text).toBe('One'); }); @@ -584,7 +611,7 @@ describe('Select', () => { await pageObject.openAndSetFilterText('One'); // Matches 'One' pageObject.pressEnterKey(); - await pageObject.clickSelect(); + pageObject.clickSelect(); currentSelection = pageObject.getSelectedOption(); expect(currentSelection?.selected).toBeTrue(); }); @@ -641,10 +668,8 @@ describe('Select', () => { }); it('pressing <Space> after dropdown is open will enter " " as filter text and keep dropdown open', async () => { - await pageObject.clickSelect(); - await waitForUpdatesAsync(); + pageObject.clickSelect(); await pageObject.pressSpaceKey(); - await waitForUpdatesAsync(); expect(element.open).toBeTrue(); expect(pageObject.getFilterInputText()).toBe(' '); }); @@ -652,7 +677,7 @@ describe('Select', () => { it('opening dropdown after applying filter previously starts with empty filter', async () => { await pageObject.openAndSetFilterText('T'); // Matches 'Two' and 'Three' await pageObject.closeDropdown(); - await pageObject.clickSelect(); + pageObject.clickSelect(); expect(pageObject.getFilterInputText()).toBe(''); expect(pageObject.getFilteredOptions().length).toBe(6); @@ -669,7 +694,7 @@ describe('Select', () => { }); it('opening dropdown with no filter does not display "not items found" element', async () => { - await pageObject.clickSelect(); + await clickAndWaitForOpen(element); expect(pageObject.isNoResultsLabelVisible()).toBeFalse(); }); @@ -723,7 +748,7 @@ describe('Select', () => { }); it('clicking in filter input after dropdown is open, does not close dropdown', async () => { - await pageObject.clickSelect(); + await clickAndWaitForOpen(element); await pageObject.clickFilterInput(); expect(element.open).toBeTrue(); }); @@ -736,7 +761,7 @@ describe('Select', () => { }); it('filter input "aria-controls" and "aria-activedescendant" attributes are set to element state', async () => { - await pageObject.clickSelect(); + await clickAndWaitForOpen(element); const filterInput = element.shadowRoot?.querySelector('.filter-input'); expect(filterInput?.getAttribute('aria-controls')).toBe( element.ariaControls @@ -821,10 +846,9 @@ describe('Select', () => { await disconnect(); }); - it('exercise clickOptionWithDisplayText', async () => { - await pageObject.clickSelect(); - await waitForUpdatesAsync(); - await pageObject.clickOptionWithDisplayText('Two'); + it('exercise clickOptionWithDisplayText', () => { + pageObject.clickSelect(); + pageObject.clickOptionWithDisplayText('Two'); expect(element.value).toBe('two'); expect(element.selectedIndex).toBe(1); }); diff --git a/packages/nimble-tokens/CHANGELOG.json b/packages/nimble-tokens/CHANGELOG.json index b9caa9a88d..4b32d99095 100644 --- a/packages/nimble-tokens/CHANGELOG.json +++ b/packages/nimble-tokens/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@ni/nimble-tokens", "entries": [ + { + "date": "Tue, 12 Mar 2024 21:01:54 GMT", + "version": "6.12.1", + "tag": "@ni/nimble-tokens_v6.12.1", + "comments": { + "patch": [ + { + "author": "7282195+m-akinc@users.noreply.github.com", + "package": "@ni/nimble-tokens", + "commit": "bc825e1b057eafd8bc005d11e9a224aa9aee9619", + "comment": "Update typescript to 4.9.5" + } + ] + } + }, { "date": "Wed, 06 Mar 2024 17:56:10 GMT", "version": "6.12.0", diff --git a/packages/nimble-tokens/CHANGELOG.md b/packages/nimble-tokens/CHANGELOG.md index fbb4e1919e..1640ba454b 100644 --- a/packages/nimble-tokens/CHANGELOG.md +++ b/packages/nimble-tokens/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @ni/nimble-tokens -This log was last generated on Wed, 28 Feb 2024 19:35:04 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. <!-- Start content --> +## 6.12.1 + +Tue, 12 Mar 2024 21:01:54 GMT + +### Patches + +- Update typescript to 4.9.5 ([ni/nimble@bc825e1](https://github.com/ni/nimble/commit/bc825e1b057eafd8bc005d11e9a224aa9aee9619)) + ## 6.12.0 Wed, 28 Feb 2024 19:35:04 GMT diff --git a/packages/nimble-tokens/package.json b/packages/nimble-tokens/package.json index 7d6d61c119..c44fcb3619 100644 --- a/packages/nimble-tokens/package.json +++ b/packages/nimble-tokens/package.json @@ -1,6 +1,6 @@ { "name": "@ni/nimble-tokens", - "version": "6.12.0", + "version": "6.12.1", "description": "Design tokens for the NI Nimble Design System", "scripts": { "build": "npm run build:svg-to-ts && npm run build:ts && npm run build:svg-to-ico && npm run build:generate-font-scss && npm run build:style-dictionary", @@ -46,7 +46,7 @@ "style-dictionary": "^3.9.2", "svg-to-ts": "^12.0.0", "to-ico": "^1.1.5", - "typescript": "~4.8.2" + "typescript": "~4.9.5" }, "files": [ "dist/styledictionary/css/**", diff --git a/packages/performance/.eslintrc.js b/packages/performance/.eslintrc.cjs similarity index 75% rename from packages/performance/.eslintrc.js rename to packages/performance/.eslintrc.cjs index 3e1be2eb59..f7f5374ae5 100644 --- a/packages/performance/.eslintrc.js +++ b/packages/performance/.eslintrc.cjs @@ -27,5 +27,11 @@ module.exports = { // Rules enabled due to strictNullChecks '@typescript-eslint/no-non-null-assertion': 'off', } + }, { + files: ['vite.config.js'], + rules: { + // Configuration scripts will not be in published package and are allowed to use devDependencies + 'import/no-extraneous-dependencies': ['error', { devDependencies: true }], + } }] }; diff --git a/packages/performance/lighthouserc.js b/packages/performance/lighthouserc.cjs similarity index 100% rename from packages/performance/lighthouserc.js rename to packages/performance/lighthouserc.cjs diff --git a/packages/performance/package.json b/packages/performance/package.json index 56c255a00b..a2b5b6af64 100644 --- a/packages/performance/package.json +++ b/packages/performance/package.json @@ -2,6 +2,7 @@ "name": "@ni-private/performance", "version": "1.0.0", "private": true, + "type": "module", "description": "A package to measure the performance of the Nimble Components", "scripts": { "build": "tsc && vite build src", @@ -15,7 +16,7 @@ }, "devDependencies": { "@lhci/cli": "^0.13.0", - "typescript": "~4.8.2", + "typescript": "~4.9.5", "vite": "^5.1.5" }, "files": [ diff --git a/packages/performance/src/vite.config.js b/packages/performance/src/vite.config.js index 2a8b74e794..8f5bec56a9 100644 --- a/packages/performance/src/vite.config.js +++ b/packages/performance/src/vite.config.js @@ -1,4 +1,3 @@ -// eslint-disable-next-line import/no-extraneous-dependencies import { defineConfig } from 'vite'; import { resolve } from 'path'; diff --git a/packages/site/package.json b/packages/site/package.json index 8788e7c94f..9f443eac51 100644 --- a/packages/site/package.json +++ b/packages/site/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "@11ty/eleventy": "^2.0.1", - "typescript": "~4.8.2", + "typescript": "~4.9.5", "vite": "^5.1.5" }, "files": [ diff --git a/packages/xliff-to-json-converter/CHANGELOG.json b/packages/xliff-to-json-converter/CHANGELOG.json index 7f91727566..005266fb38 100644 --- a/packages/xliff-to-json-converter/CHANGELOG.json +++ b/packages/xliff-to-json-converter/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@ni/xliff-to-json-converter", "entries": [ + { + "date": "Tue, 12 Mar 2024 21:01:54 GMT", + "version": "1.1.5", + "tag": "@ni/xliff-to-json-converter_v1.1.5", + "comments": { + "patch": [ + { + "author": "7282195+m-akinc@users.noreply.github.com", + "package": "@ni/xliff-to-json-converter", + "commit": "bc825e1b057eafd8bc005d11e9a224aa9aee9619", + "comment": "Update typescript to 4.9.5" + } + ] + } + }, { "date": "Wed, 06 Mar 2024 17:56:10 GMT", "version": "1.1.4", diff --git a/packages/xliff-to-json-converter/CHANGELOG.md b/packages/xliff-to-json-converter/CHANGELOG.md index cbf7b1de84..6d24ded3ae 100644 --- a/packages/xliff-to-json-converter/CHANGELOG.md +++ b/packages/xliff-to-json-converter/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @ni/xliff-to-json-converter -This log was last generated on Thu, 22 Feb 2024 13:39:47 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. <!-- Start content --> +## 1.1.5 + +Tue, 12 Mar 2024 21:01:54 GMT + +### Patches + +- Update typescript to 4.9.5 ([ni/nimble@bc825e1](https://github.com/ni/nimble/commit/bc825e1b057eafd8bc005d11e9a224aa9aee9619)) + ## 1.1.4 Thu, 22 Feb 2024 13:39:47 GMT diff --git a/packages/xliff-to-json-converter/package.json b/packages/xliff-to-json-converter/package.json index 2213e87371..565897f7a8 100644 --- a/packages/xliff-to-json-converter/package.json +++ b/packages/xliff-to-json-converter/package.json @@ -1,6 +1,6 @@ { "name": "@ni/xliff-to-json-converter", - "version": "1.1.4", + "version": "1.1.5", "description": "A utility to convert translation files from XLIFF to JSON for Angular localization", "main": "dist/commonjs/cli.js", "bin": { @@ -34,7 +34,7 @@ "@types/yargs": "^17.0.10", "jasmine": "^5.1.0", "jasmine-core": "^5.1.2", - "typescript": "~4.8.2" + "typescript": "~4.9.5" }, "dependencies": { "xliff": "^6.1.0",