diff --git a/src/aria/combobox/combobox.spec.ts b/src/aria/combobox/combobox.spec.ts
index e2e010e01efc..4eae17061f5e 100644
--- a/src/aria/combobox/combobox.spec.ts
+++ b/src/aria/combobox/combobox.spec.ts
@@ -4,7 +4,7 @@ import {By} from '@angular/platform-browser';
import {Combobox, ComboboxInput, ComboboxPopup, ComboboxPopupContainer} from '../combobox';
import {Listbox, Option} from '../listbox';
import {runAccessibilityChecks} from '@angular/cdk/testing/private';
-import {Tree, TreeItem, TreeItemGroup, TreeItemGroupContent} from '../tree';
+import {Tree, TreeItem, TreeItemGroup} from '../tree';
import {NgTemplateOutlet} from '@angular/common';
describe('Combobox', () => {
@@ -1083,8 +1083,8 @@ class ComboboxListboxExample {
@if (node.children) {
-
-
+
+
{
- if (this._deferredContentAware.contentVisible()) {
+ if (this.deferredContentAware()?.contentVisible()) {
if (this._isRendered) return;
this._viewContainerRef.clear();
this._viewContainerRef.createEmbeddedView(this._templateRef);
this._isRendered = true;
- } else if (!this._deferredContentAware.preserveContent()) {
+ } else if (!this.deferredContentAware()?.preserveContent()) {
this._viewContainerRef.clear();
this._isRendered = false;
}
diff --git a/src/aria/tree/index.ts b/src/aria/tree/index.ts
index d441721cb5d7..f03ad4c2dbaa 100644
--- a/src/aria/tree/index.ts
+++ b/src/aria/tree/index.ts
@@ -6,4 +6,4 @@
* found in the LICENSE file at https://angular.dev/license
*/
-export {TreeItemGroup, TreeItemGroupContent, Tree, TreeItem} from './tree';
+export {TreeItemGroup, Tree, TreeItem} from './tree';
diff --git a/src/aria/tree/tree.spec.ts b/src/aria/tree/tree.spec.ts
index 5b73addf196e..94740745b4fc 100644
--- a/src/aria/tree/tree.spec.ts
+++ b/src/aria/tree/tree.spec.ts
@@ -4,7 +4,7 @@ import {ComponentFixture, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {Direction} from '@angular/cdk/bidi';
import {provideFakeDirectionality, runAccessibilityChecks} from '@angular/cdk/testing/private';
-import {Tree, TreeItem, TreeItemGroup, TreeItemGroupContent} from './tree';
+import {Tree, TreeItem, TreeItemGroup} from './tree';
interface ModifierKeys {
ctrlKey?: boolean;
@@ -19,7 +19,6 @@ describe('Tree', () => {
let treeElement: HTMLElement;
let treeInstance: Tree;
let treeItemElements: HTMLElement[];
- let treeItemGroupElements: HTMLElement[];
const keydown = (key: string, modifierKeys: ModifierKeys = {}) => {
const event = new KeyboardEvent('keydown', {key, bubbles: true, ...modifierKeys});
@@ -67,12 +66,10 @@ describe('Tree', () => {
function defineTestVariables() {
const treeDebugElement = fixture.debugElement.query(By.directive(Tree));
const treeItemDebugElements = fixture.debugElement.queryAll(By.directive(TreeItem));
- const treeItemGroupDebugElements = fixture.debugElement.queryAll(By.directive(TreeItemGroup));
treeElement = treeDebugElement.nativeElement as HTMLElement;
treeInstance = treeDebugElement.componentInstance as Tree;
treeItemElements = treeItemDebugElements.map(debugEl => debugEl.nativeElement);
- treeItemGroupElements = treeItemGroupDebugElements.map(debugEl => debugEl.nativeElement);
}
function updateTree(
@@ -131,10 +128,6 @@ describe('Tree', () => {
return treeItemElements.find(el => el.getAttribute('data-value') === String(value));
}
- function getTreeItemGroupElementByValue(value: string): HTMLElement | undefined {
- return treeItemGroupElements.find(el => el.getAttribute('data-group-for') === String(value));
- }
-
function getFocusedTreeItemValue(): string | undefined {
let item: HTMLElement | undefined;
if (testComponent.focusMode() === 'roving') {
@@ -187,14 +180,6 @@ describe('Tree', () => {
expect(getTreeItemElementByValue('blueberry')!.getAttribute('role')).toBe('treeitem');
});
- it('should correctly set the role attribute to "group" for TreeItemGroup', () => {
- expandAll();
-
- expect(getTreeItemGroupElementByValue('fruits')!.getAttribute('role')).toBe('group');
- expect(getTreeItemGroupElementByValue('vegetables')!.getAttribute('role')).toBe('group');
- expect(getTreeItemGroupElementByValue('berries')!.getAttribute('role')).toBe('group');
- });
-
it('should set aria-orientation to "vertical" by default', () => {
expect(treeElement.getAttribute('aria-orientation')).toBe('vertical');
});
@@ -262,12 +247,6 @@ describe('Tree', () => {
expect(strawberry.getAttribute('aria-setsize')).toBe('2');
expect(strawberry.getAttribute('aria-posinset')).toBe('1');
});
-
- it('should set aria-owns on expandable items pointing to their group id', () => {
- const fruitsItem = getTreeItemElementByValue('fruits')!;
- const group = getTreeItemGroupElementByValue('fruits')!;
- expect(fruitsItem.getAttribute('aria-owns')).toBe(group!.id);
- });
});
describe('custom configuration', () => {
@@ -1359,12 +1338,11 @@ interface TestTreeNode {
>
{{ node.label }}
@if (node.children !== undefined && node.children!.length > 0) {
-
-
+
+
@for (node of node.children; track node.value) {
}
@@ -1374,7 +1352,7 @@ interface TestTreeNode {
`,
- imports: [Tree, TreeItem, TreeItemGroup, TreeItemGroupContent, NgTemplateOutlet],
+ imports: [Tree, TreeItem, TreeItemGroup, NgTemplateOutlet],
})
class TestTreeComponent {
nodes = signal([
diff --git a/src/aria/tree/tree.ts b/src/aria/tree/tree.ts
index 19bd38bd3d7a..ff6e27ced87a 100644
--- a/src/aria/tree/tree.ts
+++ b/src/aria/tree/tree.ts
@@ -218,13 +218,12 @@ export class Tree {
'[attr.aria-current]': 'pattern.current()',
'[attr.aria-disabled]': 'pattern.disabled()',
'[attr.aria-level]': 'pattern.level()',
- '[attr.aria-owns]': 'ownsId()',
'[attr.aria-setsize]': 'pattern.setsize()',
'[attr.aria-posinset]': 'pattern.posinset()',
'[attr.tabindex]': 'pattern.tabindex()',
},
})
-export class TreeItem implements OnInit, OnDestroy, HasElement {
+export class TreeItem extends DeferredContentAware implements OnInit, OnDestroy, HasElement {
/** A reference to the tree item element. */
private readonly _elementRef = inject(ElementRef);
@@ -234,9 +233,6 @@ export class TreeItem implements OnInit, OnDestroy, HasElement {
/** The owned tree item group. */
private readonly _group = signal | undefined>(undefined);
- /** The id of the owned group. */
- readonly ownsId = computed(() => this._group()?.id);
-
/** The host native element. */
readonly element = computed(() => this._elementRef.nativeElement);
@@ -267,9 +263,13 @@ export class TreeItem implements OnInit, OnDestroy, HasElement {
pattern: TreeItemPattern;
constructor() {
- // Updates the visibility of the owned group.
+ super();
+ this.preserveContent.set(true);
+ // Connect the group's hidden state to the DeferredContentAware's visibility.
afterRenderEffect(() => {
- this._group()?.visible.set(this.pattern.expanded());
+ this.tree().pattern instanceof ComboboxTreePattern
+ ? this.contentVisible.set(true)
+ : this.contentVisible.set(this.pattern.expanded());
});
}
@@ -289,12 +289,7 @@ export class TreeItem implements OnInit, OnDestroy, HasElement {
id: () => this._id,
tree: treePattern,
parent: parentPattern,
- children: computed(
- () =>
- this._group()
- ?.children()
- .map(item => (item as TreeItem).pattern) ?? [],
- ),
+ children: computed(() => this._group()?.children() ?? []),
hasChildren: computed(() => !!this._group()),
});
}
@@ -314,60 +309,30 @@ export class TreeItem implements OnInit, OnDestroy, HasElement {
}
/**
- * Container that designates content as a group.
+ * Contains children tree itmes.
*/
@Directive({
- selector: '[ngTreeItemGroup]',
+ selector: 'ng-template[ngTreeItemGroup]',
exportAs: 'ngTreeItemGroup',
- hostDirectives: [
- {
- directive: DeferredContentAware,
- inputs: ['preserveContent'],
- },
- ],
- host: {
- 'class': 'ng-treeitem-group',
- 'role': 'group',
- '[id]': 'id',
- '[attr.inert]': 'visible() ? null : true',
- },
+ hostDirectives: [DeferredContent],
})
-export class TreeItemGroup implements OnInit, OnDestroy, HasElement {
- /** A reference to the group element. */
- private readonly _elementRef = inject(ElementRef);
-
- /** The DeferredContentAware host directive. */
- private readonly _deferredContentAware = inject(DeferredContentAware);
+export class TreeItemGroup implements OnInit, OnDestroy {
+ /** The DeferredContent host directive. */
+ private readonly _deferredContent = inject(DeferredContent);
/** All groupable items that are descendants of the group. */
private readonly _unorderedItems = signal(new Set>());
- /** The host native element. */
- readonly element = computed(() => this._elementRef.nativeElement);
-
- /** Unique ID for the group. */
- readonly id = inject(_IdGenerator).getId('ng-tree-group-');
-
- /** Whether the group is visible. */
- readonly visible = signal(true);
-
/** Child items within this group. */
- readonly children = computed(() => [...this._unorderedItems()].sort(sortDirectives));
+ readonly children = computed[]>(() =>
+ [...this._unorderedItems()].sort(sortDirectives).map(c => c.pattern),
+ );
/** Tree item that owns the group. */
readonly ownedBy = input.required>();
- constructor() {
- this._deferredContentAware.preserveContent.set(true);
- // Connect the group's hidden state to the DeferredContentAware's visibility.
- afterRenderEffect(() => {
- this.ownedBy().tree().pattern instanceof ComboboxTreePattern
- ? this._deferredContentAware.contentVisible.set(true)
- : this._deferredContentAware.contentVisible.set(this.visible());
- });
- }
-
ngOnInit() {
+ this._deferredContent.deferredContentAware.set(this.ownedBy());
this.ownedBy().register(this);
}
@@ -385,13 +350,3 @@ export class TreeItemGroup implements OnInit, OnDestroy, HasElement {
this._unorderedItems.set(new Set(this._unorderedItems()));
}
}
-
-/**
- * A structural directive that marks the `ng-template` to be used as the content
- * for a `TreeItemGroup`. This content can be lazily loaded.
- */
-@Directive({
- selector: 'ng-template[ngTreeItemGroupContent]',
- hostDirectives: [DeferredContent],
-})
-export class TreeItemGroupContent {}
diff --git a/src/components-examples/aria/combobox/combobox-tree-auto-select/combobox-tree-auto-select-example.html b/src/components-examples/aria/combobox/combobox-tree-auto-select/combobox-tree-auto-select-example.html
index 2b296bbd117b..4ef3639366a4 100644
--- a/src/components-examples/aria/combobox/combobox-tree-auto-select/combobox-tree-auto-select-example.html
+++ b/src/components-examples/aria/combobox/combobox-tree-auto-select/combobox-tree-auto-select-example.html
@@ -49,8 +49,8 @@
@if (node.children) {
-
-
+
+
@if (node.children) {
-
-
+
+
@if (node.children) {
-
-
+
+
@if (node.children) {
-
-
+
+
@if (node.children) {
-
-
+
+
@if (node.children) {
-
-
+
+
@if (node.children) {
-
-
+
+
@if (node.children) {
-
-
+
+
@if (node.children) {
-
-
+
+
@if (node.children) {
-
-
+
+
@if (node.children) {
-
-
+
+
@if (node.children) {
-
-
+
+
@if (node.children) {
-