-
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
Conversation
src/lib/core/rtl/dir.ts
Outdated
| } from '@angular/core'; | ||
|
|
||
| import {Directionality} from './directionality'; | ||
| export {Directionality} from './directionality'; |
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.
Can you change the directory name from rtl to bidi and add an index.d.ts that re-exports the public symbols?
We should also rename the NgModule to BidiModule
src/lib/core/rtl/dir.ts
Outdated
| import {Directionality} from './directionality'; | ||
| export {Directionality} from './directionality'; | ||
|
|
||
| export type LayoutDirection = 'ltr' | 'rtl'; |
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.
What do you think of renaming LayoutDirection to just Direction now?
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.
Makes sense
src/lib/core/rtl/dir.ts
Outdated
| return { | ||
| ngModule: RtlModule, | ||
| providers: [] | ||
| providers: [Directionality] |
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.
Can you make a provider for Directionality similar to LiveAnnouncer? Something like
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]],
useFactory: function(parentDirectionality) {
return parentDirectionality || new Directionality();
}
};Has to use the function syntax rather than () => due to an open bug in the metadata extractor
(should live in directionality.ts)
src/lib/core/rtl/directionality.ts
Outdated
| */ | ||
| @Injectable() | ||
| export class Directionality { | ||
| value: LayoutDirection; |
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.
The type def for LayoutDirection should move to this file
src/lib/core/rtl/directionality.ts
Outdated
| import {LayoutDirection} from './dir'; | ||
|
|
||
| /** | ||
| * A provider to get the direction outside of angular on the `html` or `body` elements |
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.
"A provider" isn't really accurate (it's just a class that's provided). It can just be something like
The directionality (LTR / RTL) context for the application (or a subtree of it).
Exposes the current direction and a stream of direction changes.
src/lib/core/rtl/directionality.ts
Outdated
|
|
||
| constructor() { | ||
| this.value = | ||
| (document.body.getAttribute('dir') || |
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.
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". I'd like to avoid using getComputedStyle here, though, since we're already calling it for the theming check.
Let's just add a TODO to handle auto with this background info.
src/lib/core/rtl/directionality.ts
Outdated
|
|
||
| constructor() { | ||
| this.value = | ||
| (document.body.getAttribute('dir') || |
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.
This should also check for the existence of document before dereferencing it (for platform-server)
1793720 to
5e6040d
Compare
cda2cc9 to
ba756f5
Compare
|
@EladBezalel ready for another pass? |
|
Yep |
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.
Looks good, just a couple last things to address
src/lib/core/bidi/directionality.ts
Outdated
| public change = new EventEmitter<void>(); | ||
|
|
||
| constructor() { | ||
| if (document) { |
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.
I think you need typeof document here in order to not error on Angular Universal. This will need to eventually be augmented with using universal's document service.
src/lib/core/bidi/directionality.ts
Outdated
| // 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.getAttribute('dir') || |
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.
Use the dir property instead of getAttribute? I believe they should be equivalent (but would be shorter)
src/lib/core/bidi/directionality.ts
Outdated
| @Injectable() | ||
| export class Directionality { | ||
| value: Direction = 'ltr'; | ||
| public change = new EventEmitter<void>(); |
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.
Need unit tests for the following (creating a new bidi.spec.ts):
- Directionality reads
dirfrom the html element if not specified on the body - Directionality reads
dirfrom the body even it is also specified on the html element - Directionality defaults to
ltrif nothing is specified on either body or the html element -
Dirprovides itself asDirectionality -
Diremits a change event when the value changes
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.
How before the tests i can add the dir attribute to the html and the body elements?
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.
In the top-level beforeEach grab the current dir on each and then in the top-level afterEach restore it. Then you can just change it for individual blocks.
1b22426 to
d4f0d53
Compare
| function initDir () { | ||
| document.documentElement.dir = ''; | ||
| document.body.dir = ''; | ||
| } |
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.
Call this something like clearDocumentDirAttributes? Also move to the bottom of the file, after the tests but before the test components
|
|
||
| TestBed.compileComponents(); | ||
|
|
||
| initDir(); |
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.
There needs to be an afterEach that restores the dir attributes to whatever they were before.
|
|
||
| /** Test component without Dir. */ | ||
| @Component({ | ||
| selector: 'test-app-without-dir', |
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.
You don't need a selector for components instantiated via TestBed
src/lib/core/bidi/directionality.ts
Outdated
| public change = new EventEmitter<void>(); | ||
|
|
||
| constructor() { | ||
| if (typeof document) { |
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.
typeof document === 'object' && !!document.
We have an isBrowser property on Platform now for things like this, but we can adjust that later (since it involves bringing in PlatformModule)
| selector: 'dir-test', | ||
| template: `<div></div>` | ||
| }) | ||
| class DirTest { |
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.
Call this InjectsDirectionality with selector injects-directionality. You can also completely replace TestAppWithoutDir with this component.
| </div> | ||
| ` | ||
| }) | ||
| class TestAppWithDir { |
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.
Can just call this ElementWithDir
| }); | ||
| }); | ||
|
|
||
| describe('Dir directive', () => { |
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.
There should be one top-level describe block that does configureTestingModule, importing BidiModule. You can then have two sub describe blocks ("without dir directive", "with dir directive"). Then you can have a separate beforeEach that does the createComponent in each and the query for the directive you're looking for (to reduce repetition).
| describe('Dir directive', () => { | ||
| beforeEach(async(() => { | ||
| TestBed.configureTestingModule({ | ||
| declarations: [Dir, TestAppWithDir, DirTest] |
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.
FYI you shouldn't put directives you're testing directly into the declarations of the testing module; better to import the NgModule they live in for real (so BidiModule here)
| it('should provide itself as Directionality', () => { | ||
| let fixture = TestBed.createComponent(TestAppWithDir); | ||
| let testComponent = | ||
| getDebugNode(fixture.nativeElement.querySelector('dir-test')).componentInstance; |
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.
Can use By.directive instead:
const injectedDirectionality =
fixture.debugElement.query(By.directive(InjectsDirectionality)).dir;(here and elsewhere)
| return {getContainerElement: () => overlayContainerElement}; | ||
| }}, | ||
| {provide: Dir, useFactory: () => ({value: dir})}, | ||
| {provide: Directionality, useFactory: () => ({value: dir})}, |
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 a MdCommonModule that all of the components import. You can add BidiModule to the imports of the common module.
742f79f to
d712b9d
Compare
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.
LGTM
|
@EladBezalel just needs rebase |
ce40d79 to
ac874fe
Compare
|
I've rebased it |
|
Hmm, changing the actual |
- 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 angular#3600
|
Changed |
|
Caretaker note: some people were importing |
|
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
htmlandbodyelements fordirattribute and sets the Directionality service value to itfixes #3600