mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix: prevent nested expansion panels from registering to the same accordion (#11342)
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
OnDestroy,
|
||||
Optional,
|
||||
ChangeDetectorRef,
|
||||
SkipSelf,
|
||||
} from '@angular/core';
|
||||
import {UniqueSelectionDispatcher} from '@angular/cdk/collections';
|
||||
import {CdkAccordion} from './accordion';
|
||||
@@ -30,6 +31,11 @@ let nextId = 0;
|
||||
@Directive({
|
||||
selector: 'cdk-accordion-item, [cdkAccordionItem]',
|
||||
exportAs: 'cdkAccordionItem',
|
||||
providers: [
|
||||
// Provide CdkAccordion as undefined to prevent nested accordion items from registering
|
||||
// to the same accordion.
|
||||
{provide: CdkAccordion, useValue: undefined},
|
||||
],
|
||||
})
|
||||
export class CdkAccordionItem implements OnDestroy {
|
||||
/** Subscription to openAll/closeAll events. */
|
||||
@@ -90,7 +96,7 @@ export class CdkAccordionItem implements OnDestroy {
|
||||
/** Unregister function for _expansionDispatcher. */
|
||||
private _removeUniqueSelectionListener: () => void = () => {};
|
||||
|
||||
constructor(@Optional() public accordion: CdkAccordion,
|
||||
constructor(@Optional() @SkipSelf() public accordion: CdkAccordion,
|
||||
private _changeDetectorRef: ChangeDetectorRef,
|
||||
protected _expansionDispatcher: UniqueSelectionDispatcher) {
|
||||
this._removeUniqueSelectionListener =
|
||||
|
||||
@@ -12,7 +12,8 @@ describe('CdkAccordion', () => {
|
||||
CdkAccordionModule
|
||||
],
|
||||
declarations: [
|
||||
SetOfItems
|
||||
SetOfItems,
|
||||
NestedItems,
|
||||
],
|
||||
});
|
||||
TestBed.compileComponents();
|
||||
@@ -53,6 +54,14 @@ describe('CdkAccordion', () => {
|
||||
expect(firstPanel.expanded).toBeTruthy();
|
||||
expect(secondPanel.expanded).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not register nested items to the same accordion', () => {
|
||||
const fixture = TestBed.createComponent(NestedItems);
|
||||
const innerItem = fixture.componentInstance.innerItem;
|
||||
const outerItem = fixture.componentInstance.outerItem;
|
||||
|
||||
expect(innerItem.accordion).not.toBe(outerItem.accordion);
|
||||
});
|
||||
});
|
||||
|
||||
@Component({template: `
|
||||
@@ -65,3 +74,15 @@ class SetOfItems {
|
||||
@ViewChild('item2') item2;
|
||||
multi: boolean = false;
|
||||
}
|
||||
|
||||
|
||||
@Component({template: `
|
||||
<cdk-accordion>
|
||||
<cdk-accordion-item #outerItem="cdkAccordionItem">
|
||||
<cdk-accordion-item #innerItem="cdkAccordionItem"></cdk-accordion-item>
|
||||
</cdk-accordion-item>
|
||||
</cdk-accordion>`})
|
||||
class NestedItems {
|
||||
@ViewChild('outerItem') outerItem: CdkAccordionItem;
|
||||
@ViewChild('innerItem') innerItem: CdkAccordionItem;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ import {async, TestBed} from '@angular/core/testing';
|
||||
import {Component, ViewChild} from '@angular/core';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {MatExpansionModule, MatAccordion} from './index';
|
||||
import {MatExpansionModule, MatAccordion, MatExpansionPanel} from './index';
|
||||
|
||||
|
||||
describe('CdkAccordion', () => {
|
||||
describe('MatAccordion', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
@@ -13,7 +13,8 @@ describe('CdkAccordion', () => {
|
||||
MatExpansionModule
|
||||
],
|
||||
declarations: [
|
||||
SetOfItems
|
||||
NestedPanel,
|
||||
SetOfItems,
|
||||
],
|
||||
});
|
||||
TestBed.compileComponents();
|
||||
@@ -84,6 +85,14 @@ describe('CdkAccordion', () => {
|
||||
expect(panels[0].classes['mat-expanded']).toBeFalsy();
|
||||
expect(panels[1].classes['mat-expanded']).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not register nested panels to the same accordion', () => {
|
||||
const fixture = TestBed.createComponent(NestedPanel);
|
||||
const innerPanel = fixture.componentInstance.innerPanel;
|
||||
const outerPanel = fixture.componentInstance.outerPanel;
|
||||
|
||||
expect(innerPanel.accordion).not.toBe(outerPanel.accordion);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -106,3 +115,18 @@ class SetOfItems {
|
||||
secondPanelExpanded: boolean = false;
|
||||
secondPanelDisabled: boolean = false;
|
||||
}
|
||||
|
||||
@Component({template: `
|
||||
<mat-accordion>
|
||||
<mat-expansion-panel #outerPanel="matExpansionPanel">
|
||||
<mat-expansion-panel-header>Outer Panel</mat-expansion-panel-header>
|
||||
<mat-expansion-panel #innerPanel="matExpansionPanel">
|
||||
<mat-expansion-panel-header>Inner Panel</mat-expansion-panel-header>
|
||||
<p>Content</p>
|
||||
</mat-expansion-panel>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>`})
|
||||
class NestedPanel {
|
||||
@ViewChild('outerPanel') outerPanel: MatExpansionPanel;
|
||||
@ViewChild('innerPanel') innerPanel: MatExpansionPanel;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
OnDestroy,
|
||||
Optional,
|
||||
SimpleChanges,
|
||||
SkipSelf,
|
||||
ViewContainerRef,
|
||||
ViewEncapsulation,
|
||||
} from '@angular/core';
|
||||
@@ -56,6 +57,11 @@ let uniqueId = 0;
|
||||
inputs: ['disabled', 'expanded'],
|
||||
outputs: ['opened', 'closed', 'expandedChange'],
|
||||
animations: [matExpansionAnimations.bodyExpansion],
|
||||
providers: [
|
||||
// Provide MatAccordion as undefined to prevent nested expansion panels from registering
|
||||
// to the same accordion.
|
||||
{provide: MatAccordion, useValue: undefined},
|
||||
],
|
||||
host: {
|
||||
'class': 'mat-expansion-panel',
|
||||
'[class.mat-expanded]': 'expanded',
|
||||
@@ -87,7 +93,7 @@ export class MatExpansionPanel extends CdkAccordionItem
|
||||
/** ID for the associated header element. Used for a11y labelling. */
|
||||
_headerId = `mat-expansion-panel-header-${uniqueId++}`;
|
||||
|
||||
constructor(@Optional() accordion: MatAccordion,
|
||||
constructor(@Optional() @SkipSelf() accordion: MatAccordion,
|
||||
_changeDetectorRef: ChangeDetectorRef,
|
||||
_uniqueSelectionDispatcher: UniqueSelectionDispatcher,
|
||||
private _viewContainerRef: ViewContainerRef) {
|
||||
|
||||
Reference in New Issue
Block a user