Commit e664cc527c33e23e82d8d0680978692a7b078b2c
1 parent
8bd5efa2
Exists in
master
and in
1 other branch
added body-state-classes service which adds css classes to the body with some app state informations
Showing
7 changed files
with
222 additions
and
6 deletions
Show diff stats
... | ... | @@ -0,0 +1,41 @@ |
1 | +import { | |
2 | +HtmlUtils | |
3 | +} from "./html-utils"; | |
4 | + | |
5 | +describe("HtmlUtils", () => { | |
6 | + let element: ng.IAugmentedJQuery; | |
7 | + | |
8 | + beforeEach(inject(($compile: ng.ICompileService, $rootScope: ng.IRootScopeService) => { | |
9 | + element = $compile("<a></a>")($rootScope); | |
10 | + })); | |
11 | + | |
12 | + it("removes a css class matching some prefix from an HTML Element", () => { | |
13 | + element.addClass("noosfero-class1"); | |
14 | + element.addClass("noosfero-class2"); | |
15 | + element.addClass("another-class"); | |
16 | + | |
17 | + HtmlUtils.removeCssClassByPrefix(element[0], "noosfero-"); | |
18 | + | |
19 | + // we expect classes prefixed with 'noosfero-' to be removed | |
20 | + expect(element[0].classList).not.toContain("noosfero-class1"); | |
21 | + expect(element[0].classList).not.toContain("noosfero-class2"); | |
22 | + | |
23 | + // class not prefixed with 'noosfero-' should be there | |
24 | + expect(element[0].classList).toContain("another-class"); | |
25 | + }); | |
26 | + | |
27 | + it("removes a css class matching some suffix from an HTML Element", () => { | |
28 | + element.addClass("class-1-noosfero"); | |
29 | + element.addClass("class-2-noosfero"); | |
30 | + element.addClass("another-class"); | |
31 | + | |
32 | + HtmlUtils.removeCssClassBySuffix(element[0], "-noosfero"); | |
33 | + | |
34 | + // we expect classes prefixed with 'noosfero-' to be removed | |
35 | + expect(element[0].classList).not.toContain("class-1-noosfero"); | |
36 | + expect(element[0].classList).not.toContain("class-2-noosfero"); | |
37 | + | |
38 | + // class not suffixed with '-noosfero' should be there | |
39 | + expect(element[0].classList).toContain("another-class"); | |
40 | + }); | |
41 | +}); | |
0 | 42 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,18 @@ |
1 | +export namespace HtmlUtils { | |
2 | + | |
3 | + /** | |
4 | + * Remove All Css Classes which matches some prefix | |
5 | + */ | |
6 | + export function removeCssClassByPrefix(el: HTMLElement, prefix: string) { | |
7 | + let regx = new RegExp('\\b' + prefix + '\\S*', 'g'); | |
8 | + el.className = el.className.replace(regx, ''); | |
9 | + } | |
10 | + | |
11 | + /** | |
12 | + * Remove All Css Classes which matches some suffix | |
13 | + */ | |
14 | + export function removeCssClassBySuffix(el: HTMLElement, suffix: string) { | |
15 | + let regx = new RegExp('\\S+' + suffix + '\\S*', 'g'); | |
16 | + el.className = el.className.replace(regx, ''); | |
17 | + } | |
18 | +} | |
0 | 19 | \ No newline at end of file | ... | ... |
src/app/layout/services/body-state-classes.service.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,63 @@ |
1 | +import {provideFilters} from '../../../spec/helpers'; | |
2 | +import {BodyStateClassesService} from "./body-state-classes.service"; | |
3 | +import {AuthService} from "./../../login/auth.service"; | |
4 | + | |
5 | +describe("BodyStateClasses Service", () => { | |
6 | + let currentStateName = "main"; | |
7 | + let bodyStateClasseService: BodyStateClassesService; | |
8 | + let $rootScope: ng.IRootScopeService = <any>{}, | |
9 | + $document: ng.IDocumentService = <any>{}, | |
10 | + $state: ng.ui.IStateService = <any>{ | |
11 | + current: { | |
12 | + name: currentStateName | |
13 | + } | |
14 | + }, | |
15 | + authService: AuthService = <any>{ | |
16 | + isAuthenticated: () => false | |
17 | + }, | |
18 | + bodyEl: { className: string } = { className: "" }, | |
19 | + bodyElJq: any = [bodyEl]; | |
20 | + | |
21 | + let getService = (): BodyStateClassesService => { | |
22 | + return new BodyStateClassesService($rootScope, $document, $state, authService); | |
23 | + }; | |
24 | + | |
25 | + it("should add the class noosfero-user-logged to the body element if the user is authenticated", () => { | |
26 | + authService.isAuthenticated = jasmine.createSpy("isAuthenticated").and.returnValue(true); | |
27 | + $rootScope.$on = jasmine.createSpy("$on"); | |
28 | + | |
29 | + let service = getService(); | |
30 | + | |
31 | + bodyElJq.addClass = jasmine.createSpy("addClass"); | |
32 | + bodyElJq.removeClass = jasmine.createSpy("removeClass"); | |
33 | + service["bodyElement"] = bodyElJq; | |
34 | + | |
35 | + service.start(); | |
36 | + expect(bodyElJq.addClass).toHaveBeenCalledWith(BodyStateClassesService.USER_LOGGED_CLASSNAME); | |
37 | + }); | |
38 | + | |
39 | + it("should add the class noosfero-route-[currentStateName] the body element", () => { | |
40 | + $rootScope.$on = jasmine.createSpy("$on"); | |
41 | + let service = getService(); | |
42 | + | |
43 | + bodyElJq.addClass = jasmine.createSpy("addClass"); | |
44 | + bodyElJq.removeClass = jasmine.createSpy("removeClass"); | |
45 | + service["bodyElement"] = bodyElJq; | |
46 | + | |
47 | + service.start(); | |
48 | + let stateClassName = BodyStateClassesService.ROUTE_STATE_CLASSNAME_PREFIX + currentStateName; | |
49 | + expect(bodyElJq.addClass).toHaveBeenCalledWith(stateClassName); | |
50 | + }); | |
51 | + | |
52 | + it("should capture loginSuccess event and add noosfero-user-logged class to the body element", () => { | |
53 | + pending("Test not yet implemented!"); | |
54 | + }); | |
55 | + | |
56 | + it("should capture logoutSuccess event and remove noosfero-user-logged class to the body element", () => { | |
57 | + pending("Test not yet implemented!"); | |
58 | + }); | |
59 | + | |
60 | + it("should capture $stateChangeSuccess event and switch route class in the body element", () => { | |
61 | + pending("Test not yet implemented!"); | |
62 | + }); | |
63 | +}); | |
0 | 64 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,93 @@ |
1 | +import {Directive, Inject, Injectable} from "ng-forward"; | |
2 | +import {AUTH_EVENTS} from "./../../login/auth-events"; | |
3 | +import {AuthService} from "./../../login/auth.service"; | |
4 | +import {HtmlUtils} from "../html-utils"; | |
5 | + | |
6 | +/** | |
7 | + * This is a service which adds classes to the body element | |
8 | + * indicating some app states information as | |
9 | + * eg: | |
10 | + * User Logged: | |
11 | + * - noosfero-user-logged | |
12 | + * Route States: | |
13 | + * - noosfero-route-main | |
14 | + * - noosfero-route-main.profile.info | |
15 | + */ | |
16 | +@Injectable() | |
17 | +@Inject("$rootScope", "$document", "$state", AuthService) | |
18 | +export class BodyStateClassesService { | |
19 | + | |
20 | + public static get USER_LOGGED_CLASSNAME(): string { return "noosfero-user-logged"; } | |
21 | + public static get ROUTE_STATE_CLASSNAME_PREFIX(): string { return "noosfero-route-"; } | |
22 | + | |
23 | + private bodyElement: ng.IAugmentedJQuery = null; | |
24 | + | |
25 | + constructor( | |
26 | + private $rootScope: ng.IRootScopeService, | |
27 | + private $document: ng.IDocumentService, | |
28 | + private $state: ng.ui.IStateService, | |
29 | + private authService: AuthService | |
30 | + ) { | |
31 | + | |
32 | + } | |
33 | + | |
34 | + start() { | |
35 | + this.setupUserLoggedClassToggle(); | |
36 | + this.setupStateClassToggle(); | |
37 | + } | |
38 | + | |
39 | + getStateChangeSuccessHandlerFunction(bodyElement: ng.IAugmentedJQuery): (event: ng.IAngularEvent, toState: ng.ui.IState) => void { | |
40 | + let self = this; | |
41 | + return (event: ng.IAngularEvent, toState: ng.ui.IState) => { | |
42 | + self.switchStateClasses(bodyElement, BodyStateClassesService.ROUTE_STATE_CLASSNAME_PREFIX); | |
43 | + }; | |
44 | + } | |
45 | + | |
46 | + switchStateClasses(bodyElement: ng.IAugmentedJQuery, state: ng.ui.IState) { | |
47 | + HtmlUtils.removeCssClassByPrefix(bodyElement[0], BodyStateClassesService.ROUTE_STATE_CLASSNAME_PREFIX); | |
48 | + bodyElement.addClass(BodyStateClassesService.ROUTE_STATE_CLASSNAME_PREFIX + state.name); | |
49 | + } | |
50 | + | |
51 | + /** | |
52 | + * Setup the initial class name on body element indicating the current route | |
53 | + * and adds event handler to swith this class when the current page/state changes | |
54 | + */ | |
55 | + private setupStateClassToggle() { | |
56 | + let bodyElement = this.getBodyElement(); | |
57 | + bodyElement.addClass(BodyStateClassesService.ROUTE_STATE_CLASSNAME_PREFIX + this.$state.current.name); | |
58 | + this.$rootScope.$on("$stateChangeSuccess", this.getStateChangeSuccessHandlerFunction(bodyElement)); | |
59 | + } | |
60 | + | |
61 | + /** | |
62 | + * Setup the initial state of the user-logged css class | |
63 | + * and adds events handlers to switch this class when the login events happens | |
64 | + */ | |
65 | + private setupUserLoggedClassToggle() { | |
66 | + let bodyElement = this.getBodyElement(); | |
67 | + | |
68 | + // get initial logged information from the AuthService | |
69 | + // add add the css class when the user is authenticated | |
70 | + if (this.authService.isAuthenticated()) { | |
71 | + bodyElement.addClass(BodyStateClassesService.USER_LOGGED_CLASSNAME); | |
72 | + } | |
73 | + | |
74 | + // listen to the AUTH_EVENTS.loginSuccess and AUTH_EVENTS.logoutSuccess | |
75 | + // to switch the css class which indicates user logged in | |
76 | + this.$rootScope.$on(AUTH_EVENTS.loginSuccess, () => { | |
77 | + bodyElement.addClass(BodyStateClassesService.USER_LOGGED_CLASSNAME); | |
78 | + }); | |
79 | + this.$rootScope.$on(AUTH_EVENTS.logoutSuccess, () => { | |
80 | + bodyElement.removeClass(BodyStateClassesService.USER_LOGGED_CLASSNAME); | |
81 | + }); | |
82 | + } | |
83 | + | |
84 | + /** | |
85 | + * Returns the user 'body' html Element | |
86 | + */ | |
87 | + private getBodyElement(): ng.IAugmentedJQuery { | |
88 | + if (this.bodyElement === null) { | |
89 | + this.bodyElement = angular.element(this.$document.find("body")); | |
90 | + } | |
91 | + return this.bodyElement; | |
92 | + } | |
93 | +} | |
0 | 94 | \ No newline at end of file | ... | ... |
src/app/login/auth.service.spec.ts
src/app/main/main.component.ts
1 | -import {bundle, Component, StateConfig} from "ng-forward"; | |
1 | +import {bundle, Component, StateConfig, Inject} from "ng-forward"; | |
2 | 2 | import {ArticleBlogComponent} from "./../article/types/blog/blog.component"; |
3 | 3 | |
4 | 4 | import {ArticleViewComponent} from "./../article/article-default-view.component"; |
... | ... | @@ -20,6 +20,7 @@ import {SessionService} from "../login/session.service"; |
20 | 20 | |
21 | 21 | import {NotificationService} from "../shared/services/notification.service"; |
22 | 22 | |
23 | +import {BodyStateClassesService} from "./../layout/services/body-state-classes.service"; | |
23 | 24 | |
24 | 25 | import {Navbar} from "../layout/navbar/navbar"; |
25 | 26 | |
... | ... | @@ -41,8 +42,11 @@ import {MainBlockComponent} from "../layout/blocks/main-block/main-block.compone |
41 | 42 | templateUrl: "app/main/main.html", |
42 | 43 | providers: [AuthService, SessionService] |
43 | 44 | }) |
45 | +@Inject(BodyStateClassesService) | |
44 | 46 | export class MainContentComponent { |
45 | - | |
47 | + constructor(private bodyStateClassesService: BodyStateClassesService) { | |
48 | + bodyStateClassesService.start(); | |
49 | + } | |
46 | 50 | } |
47 | 51 | |
48 | 52 | /** |
... | ... | @@ -67,7 +71,7 @@ export class MainContentComponent { |
67 | 71 | MainBlockComponent, RecentDocumentsBlockComponent, Navbar, ProfileImageBlockComponent, |
68 | 72 | MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent |
69 | 73 | ], |
70 | - providers: [AuthService, SessionService, NotificationService] | |
74 | + providers: [AuthService, SessionService, NotificationService, BodyStateClassesService] | |
71 | 75 | }) |
72 | 76 | @StateConfig([ |
73 | 77 | { |
... | ... | @@ -90,5 +94,4 @@ export class MainContentComponent { |
90 | 94 | } |
91 | 95 | ]) |
92 | 96 | export class MainComponent { |
93 | - | |
94 | 97 | } | ... | ... |