diff --git a/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/advanced/demo.component.html b/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/advanced/demo.component.html deleted file mode 100644 index 004bf43632..0000000000 --- a/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/advanced/demo.component.html +++ /dev/null @@ -1,150 +0,0 @@ -
- -
- - - -

Modes

- -
    -
  • - - Navigation - -
  • -
  • - - Read-only - -
  • -
  • - - Selection - -
  • -
-
-
    -
  • - - - Leaf-node selection only - - -
  • -
  • - -
      -
    • - - Single-select - -
    • -
    • - - Multi-select - -
        -
      • - - - Enable cascading - - -
      • -
      -
    • -
    -
    -
  • -
-
- -
    -
  • - - Include toolbar - -
  • -
  • - - Include context menus - -
  • -
-
-
-
-
- - -
- - - - - - - - - {{ node.data.name }} - - - - - - - -
-
diff --git a/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/advanced/demo.component.ts b/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/advanced/demo.component.ts deleted file mode 100644 index 02101d9689..0000000000 --- a/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/advanced/demo.component.ts +++ /dev/null @@ -1,234 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - OnDestroy, - OnInit, - ViewChild, - inject, -} from '@angular/core'; -import { - FormBuilder, - FormControl, - FormGroup, - ReactiveFormsModule, -} from '@angular/forms'; -import { - ITreeOptions, - ITreeState, - TreeModel, - TreeModule, - TreeNode, -} from '@blackbaud/angular-tree-component'; -import { SkyAngularTreeModule } from '@skyux/angular-tree-component'; -import { SkyCheckboxModule, SkyRadioModule } from '@skyux/forms'; -import { SkyFluidGridModule, SkyFormatModule } from '@skyux/layout'; -import { SkyDropdownModule } from '@skyux/popovers'; - -import { Subject, takeUntil } from 'rxjs'; - -import { AngularTreeDemoNode } from './node'; - -@Component({ - standalone: true, - selector: 'app-demo', - templateUrl: './demo.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - CommonModule, - ReactiveFormsModule, - SkyAngularTreeModule, - SkyCheckboxModule, - SkyDropdownModule, - SkyFluidGridModule, - SkyFormatModule, - SkyRadioModule, - TreeModule, - ], -}) -export class DemoComponent implements OnInit, OnDestroy { - protected set enableCascading(value: boolean) { - this.#resetSelection(); - this.treeOptions.useTriState = value; - this.#_enableCascading = value; - - if (value) { - this.selectLeafNodesOnly = false; - } - } - - protected get enableCascading(): boolean { - return this.#_enableCascading; - } - - protected set selectLeafNodesOnly(value: boolean) { - this.#resetSelection(); - this.#_selectLeafNodesOnly = value; - - if (value) { - this.enableCascading = false; - } - } - - protected get selectLeafNodesOnly(): boolean { - return this.#_selectLeafNodesOnly; - } - - protected formGroup: FormGroup; - - protected dropdownItems: { name: string; disabled: boolean }[] = [ - { - name: 'Insert an item adjacent to {0}', - disabled: false, - }, - { name: 'Insert an item under {0}', disabled: false }, - { name: 'Move up {0}', disabled: false }, - { name: 'Move down {0}', disabled: false }, - { name: 'Move left {0}', disabled: false }, - { name: 'Move right {0}', disabled: false }, - { name: 'Edit {0}', disabled: false }, - { name: 'Delete {0}', disabled: false }, - ]; - - protected nodes: AngularTreeDemoNode[] = [ - { - name: 'Animals', - isExpanded: true, - children: [ - { - name: 'Cats', - isExpanded: true, - children: [ - { name: 'Burmese' }, - { name: 'Persian' }, - { name: 'Tabby' }, - ], - }, - { - name: 'Dogs', - isExpanded: true, - children: [ - { name: 'Beagle' }, - { name: 'German shepherd' }, - { name: 'Labrador retriever' }, - ], - }, - ], - }, - ]; - - protected readOnly = false; - protected selectSingle = false; - protected treeOptions: ITreeOptions = { - animateExpand: true, - useTriState: false, - }; - - @ViewChild(TreeModel) - private tree: TreeModel | undefined; - - #_enableCascading = false; - #_selectLeafNodesOnly = false; - - #ngUnsubscribe = new Subject(); - - readonly #changeDetectorRef = inject(ChangeDetectorRef); - - constructor() { - this.formGroup = inject(FormBuilder).group({ - treeMode: new FormControl('navigation'), - selectMode: new FormControl('multiSelect'), - selectLeafNodesOnly: new FormControl(), - enableCascading: new FormControl(), - showToolbar: new FormControl(), - showContextMenus: new FormControl(), - }); - } - - public ngOnInit(): void { - this.formGroup.valueChanges - .pipe(takeUntil(this.#ngUnsubscribe)) - .subscribe((value) => { - if (value.treeMode) { - switch (value.treeMode) { - case 'selection': - this.readOnly = false; - this.#enableSelection(true); - break; - - case 'readOnly': - this.readOnly = true; - this.#enableSelection(false); - break; - - case 'navigation': - this.readOnly = false; - this.#enableSelection(false); - break; - - default: - break; - } - } - - if (value.selectMode) { - switch (value.selectMode) { - case 'singleSelect': - this.#resetSelection(); - this.selectSingle = true; - this.enableCascading = false; - break; - - case 'multiSelect': - this.#resetSelection(); - this.selectSingle = false; - this.enableCascading = false; - break; - - default: - break; - } - } - - if (value.enableCascading) { - this.enableCascading = value.enableCascading; - } - - if (value.selectLeafNodesOnly) { - this.selectLeafNodesOnly = value.selectLeafNodesOnly; - } - - this.#changeDetectorRef.markForCheck(); - }); - } - - public ngOnDestroy(): void { - this.#ngUnsubscribe.next(); - this.#ngUnsubscribe.complete(); - } - - protected actionClicked(name: string, node: TreeNode): void { - // Add custom actions here. - alert(name.replace('{0}', node.data.name) + ' clicked!'); - } - - protected onTreeStateChange(treeModel: ITreeState): void { - // Watch for tree state changes here. - console.log(treeModel); - } - - #enableSelection(value: boolean): void { - this.#resetSelection(); - this.treeOptions.useCheckbox = value; - this.selectLeafNodesOnly = false; - this.enableCascading = false; - } - - #resetSelection(): void { - if (this.tree) { - this.tree.selectedLeafNodeIds = {}; - this.tree.activeNodeIds = {}; - } - } -} diff --git a/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/advanced/node.ts b/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/advanced/node.ts deleted file mode 100644 index cb4d1f6dce..0000000000 --- a/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/advanced/node.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface AngularTreeDemoNode { - name: string; - isExpanded?: boolean; - children?: AngularTreeDemoNode[]; -} diff --git a/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/inline-help/demo.component.html b/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/basic/demo.component.html similarity index 58% rename from apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/inline-help/demo.component.html rename to apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/basic/demo.component.html index 48b1a7e305..99a19eadfe 100644 --- a/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/inline-help/demo.component.html +++ b/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/basic/demo.component.html @@ -7,17 +7,12 @@ #treeNodeFullTemplate > - - + /> diff --git a/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/inline-help/demo.component.ts b/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/basic/demo.component.ts similarity index 82% rename from apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/inline-help/demo.component.ts rename to apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/basic/demo.component.ts index e44776cf88..1d013ecd86 100644 --- a/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/inline-help/demo.component.ts +++ b/apps/code-examples/src/app/code-examples/angular-tree-component/angular-tree/basic/demo.component.ts @@ -21,12 +21,13 @@ export class DemoComponent { { name: 'Animals', isExpanded: true, - showHelp: true, + helpPopoverContent: 'Example help content for animals.', children: [ { name: 'Cats', isExpanded: true, - showHelp: true, + helpPopoverContent: 'Example help content for cats.', + helpPopoverTitle: 'What is a cat?', children: [ { name: 'Burmese' }, { name: 'Persian' }, @@ -39,7 +40,7 @@ export class DemoComponent { children: [ { name: 'Beagle', - showHelp: true, + helpPopoverContent: 'Example help content for beagles.', }, { name: 'German shepherd' }, { name: 'Labrador retriever' }, @@ -48,8 +49,4 @@ export class DemoComponent { ], }, ]; - - protected onActionClick(): void { - alert('Help inline button clicked!'); - } } diff --git a/apps/code-examples/src/app/features/angular-tree.modules.ts b/apps/code-examples/src/app/features/angular-tree.modules.ts index e8877ae87e..3a298c04c5 100644 --- a/apps/code-examples/src/app/features/angular-tree.modules.ts +++ b/apps/code-examples/src/app/features/angular-tree.modules.ts @@ -3,17 +3,10 @@ import { RouterModule, Routes } from '@angular/router'; const routes: Routes = [ { - path: 'advanced', + path: 'basic', loadComponent: () => import( - '../code-examples/angular-tree-component/angular-tree/advanced/demo.component' - ).then((c) => c.DemoComponent), - }, - { - path: 'inline-help', - loadComponent: () => - import( - '../code-examples/angular-tree-component/angular-tree/inline-help/demo.component' + '../code-examples/angular-tree-component/angular-tree/basic/demo.component' ).then((c) => c.DemoComponent), }, ]; diff --git a/apps/code-examples/src/app/home/home.component.html b/apps/code-examples/src/app/home/home.component.html index 793a04d396..9bf85edb6e 100644 --- a/apps/code-examples/src/app/home/home.component.html +++ b/apps/code-examples/src/app/home/home.component.html @@ -66,8 +66,7 @@
  • Angular Tree
  • diff --git a/apps/e2e/angular-tree-component-storybook/src/app/angular-tree-component/angular-tree-component.component.html b/apps/e2e/angular-tree-component-storybook/src/app/angular-tree-component/angular-tree-component.component.html index a0596586db..51e5eea6ff 100644 --- a/apps/e2e/angular-tree-component-storybook/src/app/angular-tree-component/angular-tree-component.component.html +++ b/apps/e2e/angular-tree-component-storybook/src/app/angular-tree-component/angular-tree-component.component.html @@ -75,4 +75,24 @@

    Basic with toolbar, context menu and inline help

    +

    Basic with easy mode inline help

    + + + + + + + diff --git a/libs/components/angular-tree-component/package.json b/libs/components/angular-tree-component/package.json index 51658a1656..4730c379d2 100644 --- a/libs/components/angular-tree-component/package.json +++ b/libs/components/angular-tree-component/package.json @@ -21,6 +21,7 @@ "@blackbaud/angular-tree-component": "^1.0.0", "@skyux/core": "0.0.0-PLACEHOLDER", "@skyux/forms": "0.0.0-PLACEHOLDER", + "@skyux/help-inline": "0.0.0-PLACEHOLDER", "@skyux/i18n": "0.0.0-PLACEHOLDER", "@skyux/indicators": "0.0.0-PLACEHOLDER", "@skyux/layout": "0.0.0-PLACEHOLDER" diff --git a/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree-node.component.html b/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree-node.component.html index 9d440ebf12..119ee60d9e 100644 --- a/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree-node.component.html +++ b/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree-node.component.html @@ -89,9 +89,18 @@ (click)="node.mouseAction('click', $event)" > + > + @if (helpKey || helpPopoverContent) { + + } @else { + + } + diff --git a/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree-node.component.ts b/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree-node.component.ts index c3cd476935..22aec63d6e 100644 --- a/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree-node.component.ts +++ b/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree-node.component.ts @@ -6,6 +6,7 @@ import { Input, OnInit, Optional, + TemplateRef, ViewChild, inject, } from '@angular/core'; @@ -52,6 +53,31 @@ export class SkyAngularTreeNodeComponent implements AfterViewInit, OnInit { @Input() public templates: any; + /** + * A help key that identifies the global help content to display. When specified, a [help inline](https://developer.blackbaud.com/skyux/components/help-inline) button is + * placed beside the tree node label. Clicking the button invokes global help as configured by the application. + * @preview + */ + @Input() + public helpKey: string | undefined; + + /** + * The content of the help popover. When specified, a [help inline](https://developer.blackbaud.com/skyux/components/help-inline) + * button is added to the tree node. The help inline button displays a [popover](https://developer.blackbaud.com/skyux/components/popover) + * when clicked using the specified content and optional title. + * @preview + */ + @Input() + public helpPopoverContent: string | TemplateRef | undefined; + + /** + * The title of the help popover. This property only applies when `helpPopoverContent` is + * also specified. + * @preview + */ + @Input() + public helpPopoverTitle: string | undefined; + public set childFocusIndex(value: number | undefined) { if (value !== this.#_childFocusIndex) { this.#_childFocusIndex = value; diff --git a/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree-root.component.spec.ts b/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree-root.component.spec.ts index 5d687e7983..830aff9aba 100644 --- a/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree-root.component.spec.ts +++ b/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree-root.component.spec.ts @@ -6,6 +6,10 @@ import { tick, } from '@angular/core/testing'; import { SkyAppTestUtility, expect, expectAsync } from '@skyux-sdk/testing'; +import { + SkyHelpTestingController, + SkyHelpTestingModule, +} from '@skyux/core/testing'; import { SkyTreeViewFixtureComponent } from './fixtures/tree-view.fixture.component'; import { SkyTreeViewFixturesModule } from './fixtures/tree-view.fixture.module'; @@ -190,7 +194,7 @@ describe('tree view', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [SkyTreeViewFixturesModule], + imports: [SkyHelpTestingModule, SkyTreeViewFixturesModule], }); fixture = TestBed.createComponent( @@ -259,6 +263,30 @@ describe('tree view', () => { 'fa-chevron-right', ); }); + + it('should render help inline popover', () => { + component.nodes[0].helpPopoverContent = 'Example popover content.'; + component.nodes[0].helpPopoverTitle = 'Example popover title'; + + fixture.detectChanges(); + + expect( + fixture.nativeElement.querySelectorAll( + 'sky-help-inline:not(.sky-control-help)', + ).length, + ).toBe(1); + }); + + it('should render help inline when helpKey is provided', () => { + const helpController = TestBed.inject(SkyHelpTestingController); + component.nodes[0].helpKey = 'foo.html'; + fixture.detectChanges(); + + fixture.nativeElement.querySelector('.sky-help-inline')?.click(); + fixture.detectChanges(); + + helpController.expectCurrentHelpKey('foo.html'); + }); }); describe('toolbar', () => { diff --git a/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree.module.ts b/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree.module.ts index 77613a3a77..8898513719 100644 --- a/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree.module.ts +++ b/libs/components/angular-tree-component/src/lib/modules/angular-tree/angular-tree.module.ts @@ -3,6 +3,7 @@ import { NgModule } from '@angular/core'; import { TreeModule } from '@blackbaud/angular-tree-component'; import { SkyCoreAdapterService } from '@skyux/core'; import { SkyCheckboxModule } from '@skyux/forms'; +import { SkyHelpInlineModule } from '@skyux/help-inline'; import { SkyIconModule } from '@skyux/indicators'; import { SkyToolbarModule } from '@skyux/layout'; @@ -24,6 +25,7 @@ import { SkyAngularTreeWrapperComponent } from './angular-tree-wrapper.component CommonModule, SkyAngularTreeComponentResourcesModule, SkyCheckboxModule, + SkyHelpInlineModule, SkyIconModule, SkyToolbarModule, TreeModule, diff --git a/libs/components/angular-tree-component/src/lib/modules/angular-tree/fixtures/tree-view.fixture.component.html b/libs/components/angular-tree-component/src/lib/modules/angular-tree/fixtures/tree-view.fixture.component.html index f278b1e64b..6e9f41fa58 100644 --- a/libs/components/angular-tree-component/src/lib/modules/angular-tree/fixtures/tree-view.fixture.component.html +++ b/libs/components/angular-tree-component/src/lib/modules/angular-tree/fixtures/tree-view.fixture.component.html @@ -18,6 +18,9 @@ #treeNodeFullTemplate >