-
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.
feat(directionality): a provider to get the overall directionality
- Looks at the `html` and `body` elements for `dir` attribute and sets the Directionality service value to it - Whenever someone would try to inject Directionality - if there's a Dir directive up the dom tree it would be provided fixes #3600
- Loading branch information
1 parent
0aaeb69
commit 5f8c663
Showing
33 changed files
with
361 additions
and
148 deletions.
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
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,66 @@ | ||
/** | ||
* @license | ||
* Copyright Google Inc. All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import { | ||
Directive, | ||
HostBinding, | ||
Output, | ||
Input, | ||
EventEmitter | ||
} from '@angular/core'; | ||
|
||
import {Direction, Directionality} from './directionality'; | ||
|
||
/** | ||
* Directive to listen for changes of direction of part of the DOM. | ||
* | ||
* Would provide itself in case a component looks for the Directionality service | ||
*/ | ||
@Directive({ | ||
selector: '[dir]', | ||
// TODO(hansl): maybe `$implicit` isn't the best option here, but for now that's the best we got. | ||
exportAs: '$implicit', | ||
providers: [ | ||
{provide: Directionality, useExisting: Dir} | ||
] | ||
}) | ||
export class Dir implements Directionality { | ||
/** Layout direction of the element. */ | ||
_dir: Direction = 'ltr'; | ||
|
||
/** Whether the `value` has been set to its initial value. */ | ||
private _isInitialized: boolean = false; | ||
|
||
/** Event emitted when the direction changes. */ | ||
@Output('dirChange') change = new EventEmitter<void>(); | ||
|
||
/** @docs-private */ | ||
@HostBinding('attr.dir') | ||
@Input('dir') | ||
get dir(): Direction { | ||
return this._dir; | ||
} | ||
|
||
set dir(v: Direction) { | ||
let old = this._dir; | ||
this._dir = v; | ||
if (old !== this._dir && this._isInitialized) { | ||
this.change.emit(); | ||
} | ||
} | ||
|
||
/** Current layout direction of the element. */ | ||
get value(): Direction { return this.dir; } | ||
set value(v: Direction) { this.dir = v; } | ||
|
||
/** Initialize once default value has been set. */ | ||
ngAfterContentInit() { | ||
this._isInitialized = true; | ||
} | ||
} | ||
|
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,104 @@ | ||
import {async, fakeAsync, TestBed, tick} from '@angular/core/testing'; | ||
import {Component} from '@angular/core'; | ||
import {By} from '@angular/platform-browser'; | ||
import {BidiModule, Directionality, DIR_DOCUMENT} from './index'; | ||
|
||
describe('Directionality', () => { | ||
let fakeDocument: FakeDocument; | ||
|
||
beforeEach(async(() => { | ||
fakeDocument = {body: {}, documentElement: {}}; | ||
|
||
TestBed.configureTestingModule({ | ||
imports: [BidiModule], | ||
declarations: [ElementWithDir, InjectsDirectionality], | ||
providers: [{provide: DIR_DOCUMENT, useFactory: () => fakeDocument}], | ||
}).compileComponents(); | ||
})); | ||
|
||
describe('Service', () => { | ||
it('should read dir from the html element if not specified on the body', () => { | ||
fakeDocument.documentElement.dir = 'rtl'; | ||
|
||
let fixture = TestBed.createComponent(InjectsDirectionality); | ||
let testComponent = fixture.debugElement.componentInstance; | ||
|
||
expect(testComponent.dir.value).toBe('rtl'); | ||
}); | ||
|
||
it('should read dir from the body even it is also specified on the html element', () => { | ||
fakeDocument.documentElement.dir = 'ltr'; | ||
fakeDocument.body.dir = 'rtl'; | ||
|
||
let fixture = TestBed.createComponent(InjectsDirectionality); | ||
let testComponent = fixture.debugElement.componentInstance; | ||
|
||
expect(testComponent.dir.value).toBe('rtl'); | ||
}); | ||
|
||
it('should default to ltr if nothing is specified on either body or the html element', () => { | ||
let fixture = TestBed.createComponent(InjectsDirectionality); | ||
let testComponent = fixture.debugElement.componentInstance; | ||
|
||
expect(testComponent.dir.value).toBe('ltr'); | ||
}); | ||
}); | ||
|
||
describe('Dir directive', () => { | ||
it('should provide itself as Directionality', () => { | ||
let fixture = TestBed.createComponent(ElementWithDir); | ||
const injectedDirectionality = | ||
fixture.debugElement.query(By.directive(InjectsDirectionality)).componentInstance.dir; | ||
|
||
fixture.detectChanges(); | ||
|
||
expect(injectedDirectionality.value).toBe('rtl'); | ||
}); | ||
|
||
it('should emit a change event when the value changes', fakeAsync(() => { | ||
let fixture = TestBed.createComponent(ElementWithDir); | ||
const injectedDirectionality = | ||
fixture.debugElement.query(By.directive(InjectsDirectionality)).componentInstance.dir; | ||
|
||
fixture.detectChanges(); | ||
|
||
expect(injectedDirectionality.value).toBe('rtl'); | ||
expect(fixture.componentInstance.changeCount).toBe(0); | ||
|
||
fixture.componentInstance.direction = 'ltr'; | ||
|
||
fixture.detectChanges(); | ||
tick(); | ||
|
||
expect(injectedDirectionality.value).toBe('ltr'); | ||
expect(fixture.componentInstance.changeCount).toBe(1); | ||
})); | ||
}); | ||
}); | ||
|
||
|
||
@Component({ | ||
template: ` | ||
<div [dir]="direction" (dirChange)="changeCount= changeCount + 1"> | ||
<injects-directionality></injects-directionality> | ||
</div> | ||
` | ||
}) | ||
class ElementWithDir { | ||
direction = 'rtl'; | ||
changeCount = 0; | ||
} | ||
|
||
/** Test component with Dir directive. */ | ||
@Component({ | ||
selector: 'injects-directionality', | ||
template: `<div></div>` | ||
}) | ||
class InjectsDirectionality { | ||
constructor(public dir: Directionality) { } | ||
} | ||
|
||
interface FakeDocument { | ||
documentElement: {dir?: string}; | ||
body: {dir?: string}; | ||
} |
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,64 @@ | ||
/** | ||
* @license | ||
* Copyright Google Inc. All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import { | ||
EventEmitter, | ||
Injectable, | ||
Optional, | ||
SkipSelf, | ||
Inject, | ||
InjectionToken, | ||
} from '@angular/core'; | ||
import {DOCUMENT} from '@angular/platform-browser'; | ||
|
||
|
||
export type Direction = 'ltr' | 'rtl'; | ||
|
||
/** | ||
* Injection token used to inject the document into Directionality. | ||
* This is used so that the value can be faked in tests. | ||
* | ||
* We can't use the real document in tests because changing the real `dir` causes geometry-based | ||
* tests in Safari to fail. | ||
* | ||
* We also can't re-provide the DOCUMENT token from platform-brower because the unit tests | ||
* themselves use things like `querySelector` in test code. | ||
*/ | ||
export const DIR_DOCUMENT = new InjectionToken<Document>('md-dir-doc'); | ||
|
||
/** | ||
* The directionality (LTR / RTL) context for the application (or a subtree of it). | ||
* Exposes the current direction and a stream of direction changes. | ||
*/ | ||
@Injectable() | ||
export class Directionality { | ||
value: Direction = 'ltr'; | ||
change = new EventEmitter<void>(); | ||
|
||
constructor(@Optional() @Inject(DIR_DOCUMENT) _document?: any) { | ||
if (typeof _document === 'object' && !!_document) { | ||
// TODO: handle 'auto' value - | ||
// We still need to account for dir="auto". | ||
// It looks like HTMLElemenet.dir is also "auto" when that's set to the attribute, | ||
// but getComputedStyle return either "ltr" or "rtl". avoiding getComputedStyle for now | ||
// though, we're already calling it for the theming check. | ||
this.value = (_document.body.dir || _document.documentElement.dir || 'ltr') as Direction; | ||
} | ||
} | ||
} | ||
|
||
export function DIRECTIONALITY_PROVIDER_FACTORY(parentDirectionality, _document) { | ||
return parentDirectionality || new Directionality(_document); | ||
} | ||
|
||
export const DIRECTIONALITY_PROVIDER = { | ||
// If there is already a Directionality available, use that. Otherwise, provide a new one. | ||
provide: Directionality, | ||
deps: [[new Optional(), new SkipSelf(), Directionality], [new Optional(), DOCUMENT]], | ||
useFactory: DIRECTIONALITY_PROVIDER_FACTORY | ||
}; |
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,31 @@ | ||
/** | ||
* @license | ||
* Copyright Google Inc. All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {NgModule} from '@angular/core'; | ||
import {DOCUMENT} from '@angular/platform-browser'; | ||
import {Dir} from './dir'; | ||
import {DIR_DOCUMENT, Directionality, DIRECTIONALITY_PROVIDER} from './directionality'; | ||
|
||
|
||
export { | ||
Directionality, | ||
DIRECTIONALITY_PROVIDER, | ||
DIR_DOCUMENT, | ||
Direction, | ||
} from './directionality'; | ||
export {Dir} from './dir'; | ||
|
||
@NgModule({ | ||
exports: [Dir], | ||
declarations: [Dir], | ||
providers: [ | ||
{provide: DIR_DOCUMENT, useExisting: DOCUMENT}, | ||
Directionality, | ||
] | ||
}) | ||
export class BidiModule { } |
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
Oops, something went wrong.