-
Notifications
You must be signed in to change notification settings - Fork 6.8k
feat(directionality): a provider to get the overall directionality #4044
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than changing all of these tests to add the
Directionality
, we now have aMdCommonModule
that all of the components import. You can addBidiModule
to the imports of the common module.