diff --git a/src/lib/expansion/expansion-panel.ts b/src/lib/expansion/expansion-panel.ts
index 4f94c8a80d1d..aeb5b1d63252 100644
--- a/src/lib/expansion/expansion-panel.ts
+++ b/src/lib/expansion/expansion-panel.ts
@@ -60,6 +60,9 @@ export const _MatExpansionPanelMixinBase = mixinDisabled(MatExpansionPanelBase);
/** MatExpansionPanel's states. */
export type MatExpansionPanelState = 'expanded' | 'collapsed';
+/** Counter for generating unique element ids. */
+let uniqueId = 0;
+
/**
*
component.
*
@@ -120,6 +123,9 @@ export class MatExpansionPanel extends _MatExpansionPanelMixinBase
/** Portal holding the user's content. */
_portal: TemplatePortal;
+ /** ID for the associated header element. Used for a11y labelling. */
+ _headerId = `mat-expansion-panel-header-${uniqueId++}`;
+
constructor(@Optional() @Host() accordion: MatAccordion,
_changeDetectorRef: ChangeDetectorRef,
_uniqueSelectionDispatcher: UniqueSelectionDispatcher,
diff --git a/src/lib/expansion/expansion.spec.ts b/src/lib/expansion/expansion.spec.ts
index fddb687bcb6d..7e09a4057c40 100644
--- a/src/lib/expansion/expansion.spec.ts
+++ b/src/lib/expansion/expansion.spec.ts
@@ -3,6 +3,8 @@ import {Component, ViewChild} from '@angular/core';
import {By} from '@angular/platform-browser';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {MatExpansionModule, MatExpansionPanel} from './index';
+import {SPACE, ENTER} from '@angular/cdk/keycodes';
+import {dispatchKeyboardEvent} from '@angular/cdk/testing';
describe('MatExpansionPanel', () => {
@@ -71,7 +73,7 @@ describe('MatExpansionPanel', () => {
expect(fixture.componentInstance.closeCallback).toHaveBeenCalled();
});
- it('creates a unique panel id for each panel', () => {
+ it('should create a unique panel id for each panel', () => {
const fixtureOne = TestBed.createComponent(PanelWithContent);
const headerElOne = fixtureOne.nativeElement.querySelector('.mat-expansion-panel-header');
const fixtureTwo = TestBed.createComponent(PanelWithContent);
@@ -84,9 +86,58 @@ describe('MatExpansionPanel', () => {
expect(panelIdOne).not.toBe(panelIdTwo);
});
- it('should not be able to focus content while closed', fakeAsync(() => {
+ it('should set `aria-labelledby` of the content to the header id', () => {
+ const fixture = TestBed.createComponent(PanelWithContent);
+ const headerEl = fixture.nativeElement.querySelector('.mat-expansion-panel-header');
+ const contentEl = fixture.nativeElement.querySelector('.mat-expansion-panel-content');
+
+ fixture.detectChanges();
+
+ const headerId = headerEl.getAttribute('id');
+ const contentLabel = contentEl.getAttribute('aria-labelledby');
+
+ expect(headerId).toBeTruthy();
+ expect(contentLabel).toBeTruthy();
+ expect(headerId).toBe(contentLabel);
+ });
+
+ it('should set the proper role on the content element', () => {
+ const fixture = TestBed.createComponent(PanelWithContent);
+ const contentEl = fixture.nativeElement.querySelector('.mat-expansion-panel-content');
+
+ expect(contentEl.getAttribute('role')).toBe('region');
+ });
+
+ it('should toggle the panel when pressing SPACE on the header', () => {
+ const fixture = TestBed.createComponent(PanelWithContent);
+ const headerEl = fixture.nativeElement.querySelector('.mat-expansion-panel-header');
+
+ spyOn(fixture.componentInstance.panel, 'toggle');
+
+ const event = dispatchKeyboardEvent(headerEl, 'keydown', SPACE);
+
+ fixture.detectChanges();
+
+ expect(fixture.componentInstance.panel.toggle).toHaveBeenCalled();
+ expect(event.defaultPrevented).toBe(true);
+ });
+
+ it('should toggle the panel when pressing ENTER on the header', () => {
const fixture = TestBed.createComponent(PanelWithContent);
+ const headerEl = fixture.nativeElement.querySelector('.mat-expansion-panel-header');
+
+ spyOn(fixture.componentInstance.panel, 'toggle');
+ const event = dispatchKeyboardEvent(headerEl, 'keydown', ENTER);
+
+ fixture.detectChanges();
+
+ expect(fixture.componentInstance.panel.toggle).toHaveBeenCalled();
+ expect(event.defaultPrevented).toBe(true);
+ });
+
+ it('should not be able to focus content while closed', fakeAsync(() => {
+ const fixture = TestBed.createComponent(PanelWithContent);
fixture.componentInstance.expanded = true;
fixture.detectChanges();
tick(250);