-
Notifications
You must be signed in to change notification settings - Fork 6.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
10 changed files
with
250 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# MdLiveAnnouncer | ||
`MdLiveAnnouncer` is a component, which announces messages to several screenreaders. | ||
|
||
## `<md-live-announcer>` | ||
### Methods | ||
|
||
| Name | Description | | ||
| --- | --- | | ||
| `announce(message: string)` | This announces a text message to the screenreader | | ||
|
||
### Examples | ||
A basic local variable can be assigned to the `md-live-announcer` component. | ||
```html | ||
<md-live-announcer #live> | ||
<button (click)="live.announce('Hey Google')">Announce Text</button> | ||
</md-live-announcer> | ||
``` | ||
|
||
The component is also useable through the Dependency Injection. | ||
|
||
```ts | ||
export class ChildComponent { | ||
|
||
constructor(private live: MdLiveAnnouncer) { } | ||
|
||
announceText() { | ||
this.live.announce("Hey Google"); | ||
} | ||
|
||
} | ||
``` | ||
|
||
### Supported Screenreaders | ||
- JAWS (Windows) | ||
- NVDA (Windows) | ||
- VoiceOver (OSX and iOS) | ||
- TalkBack (Android) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
@import 'mixins'; | ||
|
||
.md-live-announcer { | ||
@include md-visually-hidden(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { | ||
inject, | ||
TestComponentBuilder, | ||
ComponentFixture, | ||
fakeAsync, | ||
flushMicrotasks, | ||
tick, beforeEachProviders | ||
} from 'angular2/testing'; | ||
import { | ||
it, | ||
describe, | ||
expect, | ||
beforeEach, | ||
} from '../../core/facade/testing'; | ||
import {Component} from 'angular2/core'; | ||
import {By} from 'angular2/platform/browser'; | ||
import {MdLiveAnnouncer} from './live-announcer'; | ||
import {DOM} from '../platform/dom/dom_adapter'; | ||
|
||
export function main() { | ||
describe('MdLiveAnnouncer', () => { | ||
let live: MdLiveAnnouncer; | ||
let builder: TestComponentBuilder; | ||
let announcerEl: Element; | ||
|
||
beforeEachProviders(() => [MdLiveAnnouncer]); | ||
|
||
beforeEach(inject([TestComponentBuilder, MdLiveAnnouncer], | ||
(tcb: TestComponentBuilder, _live: MdLiveAnnouncer) => { | ||
builder = tcb; | ||
live = _live; | ||
announcerEl = queryAnnouncerElement(); | ||
})); | ||
|
||
it('should correctly update the announce text', fakeAsyncTest(() => { | ||
let appFixture: ComponentFixture = null; | ||
|
||
builder.createAsync(TestApp).then(fixture => { | ||
appFixture = fixture; | ||
}); | ||
|
||
flushMicrotasks(); | ||
|
||
let buttonElement = appFixture.debugElement | ||
.query(By.css('button')).nativeElement; | ||
|
||
buttonElement.click(); | ||
|
||
// This flushes our 100ms timeout for the screenreaders. | ||
tick(100); | ||
|
||
expect(announcerEl.textContent).toBe('Test'); | ||
})); | ||
|
||
it('should correctly update the politeness attribute', fakeAsyncTest(() => { | ||
let appFixture: ComponentFixture = null; | ||
|
||
builder.createAsync(TestApp).then(fixture => { | ||
appFixture = fixture; | ||
}); | ||
|
||
flushMicrotasks(); | ||
|
||
live.announce('Hey Google', 'assertive'); | ||
|
||
// This flushes our 100ms timeout for the screenreaders. | ||
tick(100); | ||
|
||
expect(announcerEl.textContent).toBe('Hey Google'); | ||
expect(announcerEl.getAttribute('aria-live')).toBe('assertive'); | ||
})); | ||
|
||
it('should apply the aria-live value polite by default', fakeAsyncTest(() => { | ||
let appFixture: ComponentFixture = null; | ||
|
||
builder.createAsync(TestApp).then(fixture => { | ||
appFixture = fixture; | ||
}); | ||
|
||
flushMicrotasks(); | ||
|
||
live.announce('Hey Google'); | ||
|
||
// This flushes our 100ms timeout for the screenreaders. | ||
tick(100); | ||
|
||
expect(announcerEl.textContent).toBe('Hey Google'); | ||
expect(announcerEl.getAttribute('aria-live')).toBe('polite'); | ||
})); | ||
|
||
}); | ||
} | ||
|
||
function fakeAsyncTest(fn: () => void) { | ||
return inject([], fakeAsync(fn)); | ||
} | ||
|
||
function queryAnnouncerElement() { | ||
let bodyEl = DOM.getGlobalEventTarget('body'); | ||
return DOM.querySelector(bodyEl, '.md-live-announcer'); | ||
} | ||
|
||
@Component({ | ||
selector: 'test-app', | ||
template: `<button (click)="announceText('Test')">Announce</button>`, | ||
providers: [MdLiveAnnouncer], | ||
}) | ||
class TestApp { | ||
|
||
constructor(private live: MdLiveAnnouncer) {}; | ||
|
||
announceText(message: string) { | ||
this.live.announce(message); | ||
} | ||
|
||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import {Injectable} from 'angular2/core'; | ||
import {DOM} from '../../core/platform/dom/dom_adapter'; | ||
|
||
@Injectable() | ||
export class MdLiveAnnouncer { | ||
|
||
private announcerEl: Element; | ||
|
||
constructor() { | ||
this.announcerEl = this._getAnnouncerElement(); | ||
} | ||
|
||
announce(message: string, politeness = 'polite'): void { | ||
this.announcerEl.textContent = ''; | ||
|
||
this.announcerEl.setAttribute('aria-live', politeness); | ||
|
||
// This 100ms timeout is necessary for some browser + screen-reader combinations: | ||
// - Both JAWS and NVDA over IE11 will not announce anything without a non-zero timeout. | ||
// - With Chrome and IE11 with NVDA or JAWS, a repeated (identical) message won't be read a | ||
// second time without clearing and then using a non-zero delay. | ||
// (using JAWS 17 at time of this writing). | ||
setTimeout(() => this.announcerEl.textContent = message, 100); | ||
} | ||
|
||
private _getAnnouncerElement(): Element { | ||
let documentBody = DOM.getGlobalEventTarget('body'); | ||
let childNodes = DOM.childNodes(documentBody); | ||
|
||
childNodes = Array.prototype.filter | ||
.call(childNodes, (i: Node) => DOM.isElementNode(i) && DOM.hasClass(i, 'md-live-announcer')); | ||
|
||
if (childNodes.length > 0) { | ||
return <Element> childNodes[0]; | ||
} | ||
|
||
let liveEl = DOM.createElement('div'); | ||
|
||
DOM.addClass(liveEl, 'md-live-announcer'); | ||
DOM.setAttribute(liveEl, 'aria-atomic', 'true'); | ||
DOM.setAttribute(liveEl, 'aria-live', 'polite'); | ||
|
||
DOM.appendChild(documentBody, liveEl); | ||
|
||
return liveEl; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<div class="demo-live-announcer"> | ||
|
||
<button md-button (click)="announceText('Hey Google')">Announce Text</button> | ||
|
||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import {Component} from 'angular2/core'; | ||
import {MdLiveAnnouncer} from '../../core/live-announcer/live-announcer'; | ||
|
||
@Component({ | ||
selector: 'toolbar-demo', | ||
templateUrl: 'demo-app/live-announcer/live-announcer-demo.html', | ||
providers: [MdLiveAnnouncer] | ||
}) | ||
export class LiveAnnouncerDemo { | ||
|
||
constructor(private live: MdLiveAnnouncer) {} | ||
|
||
announceText(message: string) { | ||
this.live.announce(message); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters