Commit b9f6daf63e63ab7fbd937504a85d1ec69035e37d
1 parent
bbeb5848
Exists in
master
and in
11 other branches
better css definition to the design toggler. increased the test coverage to the …
…designModeToggler Component
Showing
9 changed files
with
212 additions
and
9 deletions
Show diff stats
| @@ -0,0 +1,31 @@ | @@ -0,0 +1,31 @@ | ||
| 1 | +import {DesignModeService} from './designMode.service'; | ||
| 2 | + | ||
| 3 | +describe('DesignMode Service', () => { | ||
| 4 | + let service: DesignModeService; | ||
| 5 | + | ||
| 6 | + beforeEach(() => { | ||
| 7 | + service = new DesignModeService(); | ||
| 8 | + }); | ||
| 9 | + | ||
| 10 | + it('has the designModeOn equals false as default', () => { | ||
| 11 | + expect(service.isInDesignMode()).toBeFalsy(); | ||
| 12 | + }); | ||
| 13 | + | ||
| 14 | + it('allows set the designMode value', () => { | ||
| 15 | + spyOn(service.onToggle, 'next').and.stub(); | ||
| 16 | + service.setInDesignMode(true); | ||
| 17 | + expect(service.isInDesignMode).toBeTruthy(); | ||
| 18 | + }); | ||
| 19 | + | ||
| 20 | + it('emits the onToggle event when changing the designModeOn property', () => { | ||
| 21 | + spyOn(service.onToggle, 'next').and.stub(); | ||
| 22 | + service.setInDesignMode(true); | ||
| 23 | + expect(service.onToggle.next).toHaveBeenCalled(); | ||
| 24 | + }); | ||
| 25 | + | ||
| 26 | + it('does not emit onToggle event when there is no change on designModeOn property', () => { | ||
| 27 | + spyOn(service.onToggle, 'next').and.stub(); | ||
| 28 | + service.setInDesignMode(false); | ||
| 29 | + expect(service.onToggle.next).not.toHaveBeenCalled(); | ||
| 30 | + }); | ||
| 31 | +}); | ||
| 0 | \ No newline at end of file | 32 | \ No newline at end of file |
src/app/admin/designMode.service.ts
| @@ -12,8 +12,10 @@ export class DesignModeService { | @@ -12,8 +12,10 @@ export class DesignModeService { | ||
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | setInDesignMode(value: boolean) { | 14 | setInDesignMode(value: boolean) { |
| 15 | - this.designModeOn = value; | ||
| 16 | - this.onToggle.next(this.designModeOn); | 15 | + if (this.designModeOn !== value) { |
| 16 | + this.designModeOn = value; | ||
| 17 | + this.onToggle.next(this.designModeOn); | ||
| 18 | + } | ||
| 17 | } | 19 | } |
| 18 | 20 | ||
| 19 | constructor() { | 21 | constructor() { |
| @@ -0,0 +1,59 @@ | @@ -0,0 +1,59 @@ | ||
| 1 | +import {ComponentTestHelper, createClass} from './../../spec/component-test-helper'; | ||
| 2 | +import * as helpers from './../../spec/helpers'; | ||
| 3 | +import {DesignModeTogglerComponent} from './designModeToggler.component'; | ||
| 4 | +import {DesignModeService} from './designMode.service'; | ||
| 5 | + | ||
| 6 | +describe('DesignModeToggler Component', () => { | ||
| 7 | + const htmlTemplate: string = '<noosfero-design-toggler></noosfero-design-toggler>'; | ||
| 8 | + | ||
| 9 | + let helper: ComponentTestHelper<DesignModeTogglerComponent>; | ||
| 10 | + beforeEach(() => { | ||
| 11 | + angular.mock.module('templates'); | ||
| 12 | + angular.mock.module('ngSanitize'); | ||
| 13 | + angular.mock.module('toggle-switch'); | ||
| 14 | + }); | ||
| 15 | + | ||
| 16 | + let designModeService: DesignModeService; | ||
| 17 | + beforeEach((done) => { | ||
| 18 | + designModeService = new DesignModeService(); | ||
| 19 | + let cls = createClass({ | ||
| 20 | + template: htmlTemplate, | ||
| 21 | + directives: [DesignModeTogglerComponent], | ||
| 22 | + providers: [ | ||
| 23 | + helpers.createProviderToValue('DesignModeService', designModeService) | ||
| 24 | + ] | ||
| 25 | + }); | ||
| 26 | + helper = new ComponentTestHelper<DesignModeTogglerComponent>(cls, done); | ||
| 27 | + }); | ||
| 28 | + | ||
| 29 | + it('changes css classes representing the switch is on or off', () => { | ||
| 30 | + expect(helper.debugElement.query('div.switch-animate').hasClass('switch-off')).toBeTruthy(); | ||
| 31 | + expect(helper.debugElement.query('div.switch-animate').hasClass('switch-on')).toBeFalsy(); | ||
| 32 | + helper.component.inDesignMode = true; | ||
| 33 | + helper.detectChanges(); | ||
| 34 | + expect(helper.debugElement.query('div.switch-animate').hasClass('switch-on')).toBeTruthy(); | ||
| 35 | + expect(helper.debugElement.query('div.switch-animate').hasClass('switch-off')).toBeFalsy(); | ||
| 36 | + }); | ||
| 37 | + | ||
| 38 | + it('emits event with value "true" when changing inDesignMode to On', (done) => { | ||
| 39 | + designModeService.onToggle.subscribe((designModeOn: boolean) => { | ||
| 40 | + expect(designModeOn).toBeTruthy(); | ||
| 41 | + done(); | ||
| 42 | + }); | ||
| 43 | + helper.component.inDesignMode = true; | ||
| 44 | + helper.detectChanges(); | ||
| 45 | + }); | ||
| 46 | + | ||
| 47 | + it('emits events with value "false" when changing inDesignMode to Off', (done) => { | ||
| 48 | + helper.component.inDesignMode = true; | ||
| 49 | + helper.detectChanges(); | ||
| 50 | + | ||
| 51 | + designModeService.onToggle.subscribe((designModeOn: boolean) => { | ||
| 52 | + expect(designModeOn).toBeFalsy(); | ||
| 53 | + done(); | ||
| 54 | + }); | ||
| 55 | + | ||
| 56 | + helper.component.inDesignMode = false; | ||
| 57 | + helper.detectChanges(); | ||
| 58 | + }); | ||
| 59 | +}); | ||
| 0 | \ No newline at end of file | 60 | \ No newline at end of file |
src/app/admin/designModeToggler.component.ts
| @@ -4,11 +4,12 @@ import {DesignModeService} from './designMode.service'; | @@ -4,11 +4,12 @@ import {DesignModeService} from './designMode.service'; | ||
| 4 | selector: 'noosfero-design-toggler', | 4 | selector: 'noosfero-design-toggler', |
| 5 | templateUrl: 'app/admin/designModeToggler.html' | 5 | templateUrl: 'app/admin/designModeToggler.html' |
| 6 | }) | 6 | }) |
| 7 | -@Inject(DesignModeService, '$scope') | 7 | +@Inject(DesignModeService) |
| 8 | export class DesignModeTogglerComponent { | 8 | export class DesignModeTogglerComponent { |
| 9 | 9 | ||
| 10 | + icon: string = " <i class='glyphicon glyphicon-wrench'></i> "; | ||
| 10 | 11 | ||
| 11 | - constructor(private designModeService: DesignModeService, private $scope: ng.IScope) { | 12 | + constructor(private designModeService: DesignModeService) { |
| 12 | } | 13 | } |
| 13 | 14 | ||
| 14 | private _inDesignMode: boolean = false; | 15 | private _inDesignMode: boolean = false; |
| @@ -20,5 +21,4 @@ export class DesignModeTogglerComponent { | @@ -20,5 +21,4 @@ export class DesignModeTogglerComponent { | ||
| 20 | set inDesignMode(value: boolean) { | 21 | set inDesignMode(value: boolean) { |
| 21 | this.designModeService.setInDesignMode(value); | 22 | this.designModeService.setInDesignMode(value); |
| 22 | }; | 23 | }; |
| 23 | - | ||
| 24 | } | 24 | } |
| 25 | \ No newline at end of file | 25 | \ No newline at end of file |
src/app/admin/designModeToggler.html
| @@ -3,5 +3,6 @@ | @@ -3,5 +3,6 @@ | ||
| 3 | ng-model="ctrl.inDesignMode" | 3 | ng-model="ctrl.inDesignMode" |
| 4 | on-label="{{'designMode.toggle.ON' | translate}}" | 4 | on-label="{{'designMode.toggle.ON' | translate}}" |
| 5 | off-label="{{'designMode.toggle.OFF' | translate}}" | 5 | off-label="{{'designMode.toggle.OFF' | translate}}" |
| 6 | - knob-label="{{'designMode.label' | translate}}"> | 6 | + class="switch-small" |
| 7 | + knob-label="{{ ctrl.icon + ('designMode.label' | translate) }}"> | ||
| 7 | </toggle-switch> | 8 | </toggle-switch> |
| 8 | \ No newline at end of file | 9 | \ No newline at end of file |
src/app/layout/scss/skins/_whbl.scss
| @@ -289,6 +289,31 @@ $whbl-font-color: #16191c; | @@ -289,6 +289,31 @@ $whbl-font-color: #16191c; | ||
| 289 | .pace .pace-progress { | 289 | .pace .pace-progress { |
| 290 | background-color: #fff; | 290 | background-color: #fff; |
| 291 | } | 291 | } |
| 292 | + | ||
| 293 | + noosfero-design-toggler .ats-switch .knob i { | ||
| 294 | + color: #999999; | ||
| 295 | + } | ||
| 296 | + | ||
| 297 | + .ats-switch .knob { | ||
| 298 | + padding-right: 15px; | ||
| 299 | + } | ||
| 300 | + | ||
| 301 | + .ats-switch .glyphicon { | ||
| 302 | + top: 2px; | ||
| 303 | + } | ||
| 304 | + | ||
| 305 | + .ats-switch .switch-left { | ||
| 306 | + background-color: #41b941; | ||
| 307 | + font-weight: bold; | ||
| 308 | + color: white; | ||
| 309 | + } | ||
| 310 | + | ||
| 311 | + .ats-switch .switch-right { | ||
| 312 | + background-color: #ce3b3b; | ||
| 313 | + font-weight: bold; | ||
| 314 | + color: white; | ||
| 315 | + } | ||
| 316 | + | ||
| 292 | } | 317 | } |
| 293 | .rtl.skin-whbl #content-wrapper { | 318 | .rtl.skin-whbl #content-wrapper { |
| 294 | border-left: 0; | 319 | border-left: 0; |
src/app/layout/services/body-state-classes.service.spec.ts
| @@ -4,7 +4,7 @@ import {AuthService} from "./../../login/auth.service"; | @@ -4,7 +4,7 @@ import {AuthService} from "./../../login/auth.service"; | ||
| 4 | import {AuthEvents} from "./../../login/auth-events"; | 4 | import {AuthEvents} from "./../../login/auth-events"; |
| 5 | 5 | ||
| 6 | import {EventEmitter} from 'ng-forward'; | 6 | import {EventEmitter} from 'ng-forward'; |
| 7 | - | 7 | +import {DesignModeService} from './../../admin/designMode.service'; |
| 8 | 8 | ||
| 9 | describe("BodyStateClasses Service", () => { | 9 | describe("BodyStateClasses Service", () => { |
| 10 | 10 | ||
| @@ -19,10 +19,13 @@ describe("BodyStateClasses Service", () => { | @@ -19,10 +19,13 @@ describe("BodyStateClasses Service", () => { | ||
| 19 | }, | 19 | }, |
| 20 | authService: any = helpers.mocks.authService, | 20 | authService: any = helpers.mocks.authService, |
| 21 | bodyEl: { className: string }, | 21 | bodyEl: { className: string }, |
| 22 | - bodyElJq: any; | 22 | + bodyElJq: any, |
| 23 | + designModeService = new DesignModeService(); | ||
| 24 | + | ||
| 25 | + | ||
| 23 | 26 | ||
| 24 | let getService = (): BodyStateClassesService => { | 27 | let getService = (): BodyStateClassesService => { |
| 25 | - return new BodyStateClassesService($rootScope, $document, $state, authService); | 28 | + return new BodyStateClassesService($rootScope, $document, $state, authService, designModeService); |
| 26 | }; | 29 | }; |
| 27 | 30 | ||
| 28 | beforeEach(() => { | 31 | beforeEach(() => { |
| @@ -168,4 +171,30 @@ describe("BodyStateClasses Service", () => { | @@ -168,4 +171,30 @@ describe("BodyStateClasses Service", () => { | ||
| 168 | 171 | ||
| 169 | expect(contentWrapperMock.removeClass).toHaveBeenCalledWith(BodyStateClassesService.CONTENT_WRAPPER_FULL); | 172 | expect(contentWrapperMock.removeClass).toHaveBeenCalledWith(BodyStateClassesService.CONTENT_WRAPPER_FULL); |
| 170 | }); | 173 | }); |
| 174 | + | ||
| 175 | + it("should add the class noosfero-design-on when designMode is changed to true", () => { | ||
| 176 | + let fnOnToggle: Function = null; | ||
| 177 | + designModeService.onToggle = <any> { | ||
| 178 | + subscribe: (fn: Function) => { | ||
| 179 | + fnOnToggle = fn; | ||
| 180 | + }, | ||
| 181 | + next: (value: boolean) => { | ||
| 182 | + fnOnToggle.apply(designModeService, [value]); | ||
| 183 | + } | ||
| 184 | + }; | ||
| 185 | + | ||
| 186 | + let service = getService(); | ||
| 187 | + | ||
| 188 | + bodyElJq.addClass = jasmine.createSpy("addClass"); | ||
| 189 | + bodyElJq.removeClass = jasmine.createSpy("removeClass"); | ||
| 190 | + service["bodyElement"] = bodyElJq; | ||
| 191 | + | ||
| 192 | + service.start(); | ||
| 193 | + | ||
| 194 | + debugger; | ||
| 195 | + designModeService.setInDesignMode(true); | ||
| 196 | + | ||
| 197 | + | ||
| 198 | + expect(bodyElJq.addClass).toHaveBeenCalledWith(BodyStateClassesService.DESIGN_MODE_ON_CLASSNAME); | ||
| 199 | + }); | ||
| 171 | }); | 200 | }); |
src/app/shared/components/bootstrap-switcher/bootstrap-switcher.component.ts
0 → 100644
| @@ -0,0 +1,51 @@ | @@ -0,0 +1,51 @@ | ||
| 1 | +import {Component, Input, Output, EventEmitter} from 'ng-forward'; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +export interface BootstrapSwitcherItem { | ||
| 5 | + value: any; | ||
| 6 | + label: string; | ||
| 7 | +} | ||
| 8 | +@Component({ | ||
| 9 | + selector: 'noosfero-bootstrap-switcher', | ||
| 10 | + template: ` | ||
| 11 | + <span class="switcher-label" ng-bind="ctrl.label | translate"></span> | ||
| 12 | + <div class="btn-group switcher"> | ||
| 13 | + <button ng-repeat="option in ctrl.options track by $index" | ||
| 14 | + (click)="ctrl.switcherClick(option)" | ||
| 15 | + ng-class="ctrl.getCssClassForItem(option)" | ||
| 16 | + class="btn btn-xs" ng-bind="option.label | translate"> | ||
| 17 | + </button> | ||
| 18 | + </div> | ||
| 19 | + `, | ||
| 20 | + inputs: ['label', 'options', 'defaultOption'], | ||
| 21 | + outputs: ['onSwitch'] | ||
| 22 | +}) | ||
| 23 | +export class BootstrapSwitcherComponent { | ||
| 24 | + @Input('activeClass') activeClass: string = 'active btn-danger'; | ||
| 25 | + @Input('defaultClass') defaultClass: string = 'btn-default'; | ||
| 26 | + @Input('label') label: string; | ||
| 27 | + @Input('options') options: BootstrapSwitcherItem[]; | ||
| 28 | + @Input('defaultOption') defaultOption: BootstrapSwitcherItem; | ||
| 29 | + @Output('onSwitch') onSwitch: EventEmitter<BootstrapSwitcherItem> = new EventEmitter<BootstrapSwitcherItem>(); | ||
| 30 | + | ||
| 31 | + selectedOption: BootstrapSwitcherItem = null; | ||
| 32 | + | ||
| 33 | + constructor() { } | ||
| 34 | + | ||
| 35 | + ngOnInit() { | ||
| 36 | + this.selectedOption = this.defaultOption; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + isSelectedOption(value: BootstrapSwitcherItem): boolean { | ||
| 40 | + return this.selectedOption === value; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + getCssClassForItem(value: BootstrapSwitcherItem): string { | ||
| 44 | + return this.isSelectedOption(value) ? this.activeClass : this.defaultClass; | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + switcherClick(value: BootstrapSwitcherItem) { | ||
| 48 | + this.selectedOption = value; | ||
| 49 | + this.onSwitch.next(value); | ||
| 50 | + } | ||
| 51 | +} | ||
| 0 | \ No newline at end of file | 52 | \ No newline at end of file |