Commit e664cc527c33e23e82d8d0680978692a7b078b2c
1 parent
8bd5efa2
Exists in
master
and in
30 other branches
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 | } | ... | ... |