Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion goldens/aria/accordion/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class AccordionPanel {
toggle(): void;
readonly visible: _angular_core.Signal<boolean>;
// (undocumented)
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<AccordionPanel, "[ngAccordionPanel]", ["ngAccordionPanel"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; }, {}, ["_accordionContent"], never, true, [{ directive: typeof DeferredContentAware; inputs: { "preserveContent": "preserveContent"; }; outputs: {}; }]>;
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<AccordionPanel, "[ngAccordionPanel]", ["ngAccordionPanel"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof DeferredContentAware; inputs: { "preserveContent": "preserveContent"; }; outputs: {}; }]>;
// (undocumented)
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AccordionPanel, never>;
}
Expand Down
2 changes: 1 addition & 1 deletion goldens/aria/tabs/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export class TabPanel implements OnInit, OnDestroy {
readonly value: _angular_core.InputSignal<string>;
readonly visible: _angular_core.Signal<boolean>;
// (undocumented)
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<TabPanel, "[ngTabPanel]", ["ngTabPanel"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; }, {}, ["_tabContent"], never, true, [{ directive: typeof DeferredContentAware; inputs: { "preserveContent": "preserveContent"; }; outputs: {}; }]>;
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<TabPanel, "[ngTabPanel]", ["ngTabPanel"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof DeferredContentAware; inputs: { "preserveContent": "preserveContent"; }; outputs: {}; }]>;
// (undocumented)
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TabPanel, never>;
}
Expand Down
2 changes: 1 addition & 1 deletion src/aria/accordion/accordion-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {DeferredContent} from '../private';
*
* This directive should be applied to an `ng-template` inside an `ngAccordionPanel`.
* It allows the content of the panel to be lazily rendered, improving performance
* by only creating the content when the panel is first expanded.
* by only creating the content when the panel is expanded.
*
* ```html
* <div ngAccordionPanel>
Expand Down
24 changes: 5 additions & 19 deletions src/aria/accordion/accordion-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,19 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {
Directive,
ElementRef,
afterRenderEffect,
computed,
contentChild,
inject,
input,
} from '@angular/core';
import {Directive, ElementRef, afterRenderEffect, computed, inject, input} from '@angular/core';
import {_IdGenerator} from '@angular/cdk/a11y';
import {DeferredContentAware, AccordionTriggerPattern, reportViolations} from '../private';
import {AccordionContent} from './accordion-content';

/**
* The content panel of an accordion item that is conditionally visible.
*
* This directive is a container for the content that is shown or hidden. It should
* expose a template reference that will be used by the corresponding `ngAccordionTrigger`.
* The content within the panel should be provided using an `ng-template` with the
* `ngAccordionContent` directive so that the content is not rendered on the page until the trigger
* is expanded. It applies `role="region"` for accessibility and uses the `inert` attribute to hide
* its content from assistive technologies when not visible.
* To render the content lazily, provide it using an `ng-template` with the `ngAccordionContent`
* directive so that it is not rendered on the page until the trigger is expanded. It applies
* `role="region"` for accessibility and uses the `inert` attribute to hide its content from
* assistive technologies when not visible.
*
* ```html
* <div ngAccordionPanel #panel="ngAccordionPanel">
Expand Down Expand Up @@ -65,8 +56,6 @@ export class AccordionPanel {
/** The DeferredContentAware host directive. */
private readonly _deferredContentAware = inject(DeferredContentAware);

private readonly _accordionContent = contentChild(AccordionContent);

/** A global unique identifier for the panel. */
readonly id = input(inject(_IdGenerator).getId('ng-accordion-panel-', true));

Expand Down Expand Up @@ -94,9 +83,6 @@ export class AccordionPanel {
read: () => {
const violations: string[] = [];

if (!this._accordionContent()) {
violations.push('ngAccordionPanel must have an ngAccordionContent to render.');
}
if (!this._pattern) {
violations.push('ngAccordionPanel must have an ngAccordionTrigger to control it.');
}
Expand Down
26 changes: 12 additions & 14 deletions src/aria/accordion/accordion.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,17 @@ describe('AccordionGroup', () => {
});
});

describe('eager content', () => {
beforeEach(async () => {
fixture = TestBed.createComponent(AccordionPanelWithEagerContent) as any;
await fixture.whenStable();
});

it('should render ngAccordionPanel content without ngAccordionContent', () => {
expect(fixture.nativeElement.textContent).toContain('Content');
});
});

describe('structural validations', () => {
let consoleSpy: jasmine.Spy;

Expand Down Expand Up @@ -530,19 +541,6 @@ describe('AccordionGroup', () => {
);
});

it('should warn when ngAccordionPanel is missing ngAccordionContent', () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({
imports: [AccordionPanelWithoutContent],
});
const noContentFixture = TestBed.createComponent(AccordionPanelWithoutContent);
noContentFixture.detectChanges();

expect(consoleSpy).toHaveBeenCalledWith(
'ngAccordionPanel must have an ngAccordionContent to render.',
);
});

it('should warn when ngAccordionPanel is missing controlling trigger', () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({
Expand Down Expand Up @@ -737,7 +735,7 @@ class AccordionWithNestedTrigger {}
imports: [AccordionGroup, AccordionTrigger, AccordionPanel],
changeDetection: ChangeDetectionStrategy.Eager,
})
class AccordionPanelWithoutContent {}
class AccordionPanelWithEagerContent {}

@Component({
template: `
Expand Down
2 changes: 1 addition & 1 deletion src/aria/tabs/tab-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {DeferredContent} from '../private';
*
* This structural directive should be applied to an `ng-template` within an `ngTabPanel`.
* It enables lazy loading of the tab's content, meaning the content is only rendered
* when the tab is activated for the first time.
* when the tab is activated.
*
* ```html
* <div ngTabPanel value="myTabId">
Expand Down
7 changes: 0 additions & 7 deletions src/aria/tabs/tab-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@ import {
inject,
input,
afterRenderEffect,
contentChild,
OnInit,
OnDestroy,
} from '@angular/core';
import {TabPanelPattern, DeferredContentAware, reportViolations} from '../private';
import {TABS} from './tab-tokens';
import {TabContent} from './tab-content';

/**
* A TabPanel container for the resources of layered content associated with a tab.
Expand Down Expand Up @@ -89,8 +87,6 @@ export class TabPanel implements OnInit, OnDestroy {
tab: this._tabPattern,
});

private readonly _tabContent = contentChild(TabContent);

constructor() {
// Connect the panel's hidden state to the DeferredContentAware's visibility.
afterRenderEffect({
Expand All @@ -105,9 +101,6 @@ export class TabPanel implements OnInit, OnDestroy {
read: () => {
const violations: string[] = [];

if (!this._tabContent()) {
violations.push('ngTabPanel must have an ngTabContent structural directive to render.');
}
if (!this._tabs._tabMap().has(this.value())) {
violations.push(
`ngTabPanel with value '${this.value()}' does not have a corresponding ngTab.`,
Expand Down
32 changes: 18 additions & 14 deletions src/aria/tabs/tabs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,23 @@ describe('Tabs', () => {
});
});

describe('Eager content', () => {
let eagerFixture: ComponentFixture<PanelWithEagerContentComponent>;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [provideFakeDirectionality('ltr')],
});
eagerFixture = TestBed.createComponent(PanelWithEagerContentComponent);
fixture = eagerFixture as any;
eagerFixture.detectChanges();
});

it('should render ngTabPanel content without ngTabContent', () => {
expect(eagerFixture.nativeElement.textContent).toContain('Content 1');
});
});

describe('structural validations', () => {
let consoleSpy: jasmine.Spy;

Expand Down Expand Up @@ -845,19 +862,6 @@ describe('Tabs', () => {
);
});

it('should warn when ngTabPanel is missing ngTabContent structural directive', () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({
imports: [PanelWithoutContentComponent],
});
const noContentFixture = TestBed.createComponent(PanelWithoutContentComponent);
noContentFixture.detectChanges();

expect(consoleSpy).toHaveBeenCalledWith(
'ngTabPanel must have an ngTabContent structural directive to render.',
);
});

it('should warn when duplicate values are detected inside ngTabList', () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({
Expand Down Expand Up @@ -986,7 +990,7 @@ class PanelWithoutTabComponent {}
imports: [Tabs, TabList, Tab, TabPanel],
changeDetection: ChangeDetectionStrategy.Eager,
})
class PanelWithoutContentComponent {}
class PanelWithEagerContentComponent {}

@Component({
template: `
Expand Down
Loading