Commit e664cc527c33e23e82d8d0680978692a7b078b2c

Authored by ABNER SILVA DE OLIVEIRA
1 parent 8bd5efa2
Exists in master and in 1 other branch dev-fixes

added body-state-classes service which adds css classes to the body with some app state informations

src/app/layout/directives/.git.keep 0 → 100644
src/app/layout/html-utils.spec.ts 0 → 100644
... ... @@ -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
... ...
src/app/layout/html-utils.ts 0 → 100644
... ... @@ -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
... ...
src/app/layout/services/body-state-classes.service.ts 0 → 100644
... ... @@ -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
1   -
2   -
3 1 import {AuthService, AUTH_EVENTS} from "./";
4 2  
5 3 describe("Services", () => {
... ...
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 &quot;../login/session.service&quot;;
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 &quot;../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 }
... ...