diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c2151791c..b180eac29d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## [11.33.0](https://github.com/blackbaud/skyux/compare/11.32.0...11.33.0) (2025-01-10) + + +### Features + +* **components/pages:** create testing harness for action hub ([#2962](https://github.com/blackbaud/skyux/issues/2962)) ([736e0a3](https://github.com/blackbaud/skyux/commit/736e0a30bf2f511fda4e3467e6186e1984c41997)) +* update `@skyux/icons` to 7.10.0 ([#3008](https://github.com/blackbaud/skyux/issues/3008)) ([fc355a3](https://github.com/blackbaud/skyux/commit/fc355a3aa3be7669b33826a591b5914ddc8eb98c)) + + +### Bug Fixes + +* **components/ag-grid:** handle focus for adjacent editor cells ([#2993](https://github.com/blackbaud/skyux/issues/2993)) ([c5d3171](https://github.com/blackbaud/skyux/commit/c5d31714fb40a6c55e46f79aa267f172f5d743a6)) + +## [11.32.0](https://github.com/blackbaud/skyux/compare/11.31.0...11.32.0) (2025-01-08) + + +### Features + +* **components/forms:** add method to `SkyFormErrorHarness` to get error text ([#2992](https://github.com/blackbaud/skyux/issues/2992)) ([#2994](https://github.com/blackbaud/skyux/issues/2994)) ([65aa2e8](https://github.com/blackbaud/skyux/commit/65aa2e8eda1d5f58d6156080f5c46dddeb8a19db)) +* **components/layout:** tokenize toolbar component ([#2980](https://github.com/blackbaud/skyux/issues/2980)) ([3c04ba0](https://github.com/blackbaud/skyux/commit/3c04ba0e105c6c50f917b85746efee7fab9e5e21)) + ## [10.44.3](https://github.com/blackbaud/skyux/compare/10.44.2...10.44.3) (2025-01-08) diff --git a/apps/code-examples/project.json b/apps/code-examples/project.json index 9759f16c45..86f0d73316 100644 --- a/apps/code-examples/project.json +++ b/apps/code-examples/project.json @@ -99,7 +99,8 @@ ], "styles": [ "libs/components/theme/src/lib/styles/sky.scss", - "libs/components/theme/src/lib/styles/themes/modern/styles.scss" + "libs/components/theme/src/lib/styles/themes/modern/styles.scss", + "libs/components/ag-grid/src/lib/styles/ag-grid-styles.scss" ], "scripts": [], "assets": [], diff --git a/apps/code-examples/src/app/code-examples/pages/action-hub/demo.component.html b/apps/code-examples/src/app/code-examples/pages/action-hub/demo.component.html index 0b5b2a02fd..127e29a74a 100644 --- a/apps/code-examples/src/app/code-examples/pages/action-hub/demo.component.html +++ b/apps/code-examples/src/app/code-examples/pages/action-hub/demo.component.html @@ -1,4 +1,5 @@ { + async function setupTest(): Promise<{ + actionHubHarness: SkyActionHubHarness; + fixture: ComponentFixture; + loader: HarnessLoader; + }> { + const fixture = TestBed.createComponent(DemoComponent); + const loader = TestbedHarnessEnvironment.loader(fixture); + const actionHubHarness = await loader.getHarness( + SkyActionHubHarness.with({ + dataSkyId: 'action-hub', + }), + ); + + return { actionHubHarness, fixture, loader }; + } + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [DemoComponent, SkyModalTestingModule], + }); + }); + + it('should have correct page title', async () => { + const { actionHubHarness } = await setupTest(); + + expect(await actionHubHarness.getTitle()).toBe('Active accounts'); + }); + + it('should show needs-attention items', async () => { + const { actionHubHarness } = await setupTest(); + + expect( + await actionHubHarness + .getNeedsAttentionItems() + .then( + async (items) => + await Promise.all(items.map((item) => item.getText())), + ), + ).toEqual([ + '9 updates from portal', + '8 new messages from online donation', + '7 possible duplicates from constituent lists', + '6 updates from portal', + '5 new messages from online donation', + '4 possible duplicates from constituent lists', + '3 update from portal', + '2 new messages from online donation', + '1 possible duplicate from constituent lists', + ]); + }); + + it('should show recent links', async () => { + const { actionHubHarness } = await setupTest(); + + const linkListHarness = await actionHubHarness.getRecentLinks(); + const listItems = await linkListHarness.getListItems(); + expect(await Promise.all(listItems.map((item) => item.getText()))).toEqual([ + 'Recent 1', + 'Recent 2', + 'Recent 3', + 'Recent 4', + 'Recent 5', + ]); + }); + + it('should show related links', async () => { + const { actionHubHarness } = await setupTest(); + + const linkListHarness = await actionHubHarness.getRelatedLinks(); + const listItems = await linkListHarness.getListItems(); + expect(await Promise.all(listItems.map((item) => item.getText()))).toEqual([ + 'Link 1', + 'Link 2', + 'Link 3', + ]); + }); + + it('should show settings links', async () => { + const { actionHubHarness } = await setupTest(); + + const linkListHarness = await actionHubHarness.getSettingsLinks(); + const listItems = await linkListHarness.getListItems(); + expect(await Promise.all(listItems.map((item) => item.getText()))).toEqual([ + 'Number', + 'Color', + ]); + const modalController = TestBed.inject(SkyModalTestingController); + modalController.expectNone(); + await listItems[0].click(); + const app = TestBed.inject(ApplicationRef); + app.tick(); + await app.whenStable(); + modalController.expectCount(1); + modalController.expectOpen(SettingsModalComponent); + }); +}); diff --git a/apps/code-examples/src/app/code-examples/pages/action-hub/settings-modal.component.html b/apps/code-examples/src/app/code-examples/pages/action-hub/settings-modal.component.html index 86f2890641..55469c6d8b 100644 --- a/apps/code-examples/src/app/code-examples/pages/action-hub/settings-modal.component.html +++ b/apps/code-examples/src/app/code-examples/pages/action-hub/settings-modal.component.html @@ -1,49 +1,41 @@ - - -
+ + + @for (field of fields; track field) { @switch (title) { @case ('Color') { -
- - - - -
+ + + } @default { - + } }
} - -
- - - - -
+
+ + + + +
+ diff --git a/apps/code-examples/src/app/code-examples/pages/action-hub/settings-modal.component.spec.ts b/apps/code-examples/src/app/code-examples/pages/action-hub/settings-modal.component.spec.ts new file mode 100644 index 0000000000..db15dec21d --- /dev/null +++ b/apps/code-examples/src/app/code-examples/pages/action-hub/settings-modal.component.spec.ts @@ -0,0 +1,70 @@ +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { SkyModalHarness } from '@skyux/modals/testing'; +import { SkyActionHubHarness } from '@skyux/pages/testing'; + +import { DemoComponent } from './demo.component'; +import { MODAL_TITLE } from './modal-title-token'; + +describe('SettingsModalComponent', () => { + async function setupTest(): Promise<{ + actionHubHarness: SkyActionHubHarness; + fixture: ComponentFixture; + loader: HarnessLoader; + rootLoader: HarnessLoader; + }> { + const fixture = TestBed.createComponent(DemoComponent); + const loader = TestbedHarnessEnvironment.loader(fixture); + const rootLoader = TestbedHarnessEnvironment.documentRootLoader(fixture); + const actionHubHarness = await loader.getHarness( + SkyActionHubHarness.with({ + dataSkyId: 'action-hub', + }), + ); + + return { actionHubHarness, rootLoader, fixture, loader }; + } + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [DemoComponent, NoopAnimationsModule], + providers: [ + { + provide: MODAL_TITLE, + useValue: 'Settings Modal Test', + }, + ], + }); + }); + + it('should show settings modal', async () => { + const { actionHubHarness, rootLoader } = await setupTest(); + const settingsLinksHarness = await actionHubHarness.getSettingsLinks(); + const settingsLinks = await settingsLinksHarness.getListItems(); + expect(settingsLinks).toHaveSize(2); + await settingsLinks[1].click(); + const modalHarness = await rootLoader.getHarness( + SkyModalHarness.with({ + dataSkyId: 'settings-modal', + }), + ); + expect(await modalHarness.getHeadingText()).toBe('Color'); + const consoleSpy = spyOn(console, 'log').and.stub(); + const modalElement = TestbedHarnessEnvironment.getNativeElement( + await modalHarness.host(), + ); + const submit = modalElement.querySelector('button[type="submit"]'); + expect(submit).toBeTruthy(); + expect(submit?.textContent?.trim()).toBe('Save'); + (submit as HTMLButtonElement).click(); + expect(consoleSpy).toHaveBeenCalledWith({ + 'Color 1': '', + 'Color 2': '', + 'Color 3': '', + 'Color 4': '', + 'Color 5': '', + }); + }); +}); diff --git a/apps/code-examples/src/app/code-examples/pages/action-hub/settings-modal.component.ts b/apps/code-examples/src/app/code-examples/pages/action-hub/settings-modal.component.ts index 3ac65c4ce1..ab249e9aae 100644 --- a/apps/code-examples/src/app/code-examples/pages/action-hub/settings-modal.component.ts +++ b/apps/code-examples/src/app/code-examples/pages/action-hub/settings-modal.component.ts @@ -7,7 +7,6 @@ import { ReactiveFormsModule, } from '@angular/forms'; import { SkyColorpickerModule } from '@skyux/colorpicker'; -import { SkyIdModule } from '@skyux/core'; import { SkyInputBoxModule } from '@skyux/forms'; import { SkyModalInstance, SkyModalModule } from '@skyux/modals'; @@ -20,7 +19,6 @@ import { MODAL_TITLE } from './modal-title-token'; FormsModule, ReactiveFormsModule, SkyColorpickerModule, - SkyIdModule, SkyInputBoxModule, SkyModalModule, ], diff --git a/apps/e2e/lists-storybook/src/app/filter/filter.component.html b/apps/e2e/lists-storybook/src/app/filter/filter.component.html index a591ff0447..9d71305aa4 100644 --- a/apps/e2e/lists-storybook/src/app/filter/filter.component.html +++ b/apps/e2e/lists-storybook/src/app/filter/filter.component.html @@ -13,6 +13,20 @@

Filter button

[showButtonText]="false" (filterButtonClick)="filtersActive = !filtersActive" /> + {{ ' ' }} + + {{ ' ' }} +

Filter button with text

@@ -29,6 +43,20 @@

Filter button with text

[showButtonText]="true" (filterButtonClick)="filtersActive = !filtersActive" /> + {{ ' ' }} + + {{ ' ' }} +

Filter summary

@@ -61,7 +89,7 @@

Inline filter

- +