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 @@ |
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 | 32 | \ No newline at end of file | ... | ... |
src/app/admin/designMode.service.ts
... | ... | @@ -12,8 +12,10 @@ export class DesignModeService { |
12 | 12 | } |
13 | 13 | |
14 | 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 | 21 | constructor() { | ... | ... |
... | ... | @@ -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 | 60 | \ No newline at end of file | ... | ... |
src/app/admin/designModeToggler.component.ts
... | ... | @@ -4,11 +4,12 @@ import {DesignModeService} from './designMode.service'; |
4 | 4 | selector: 'noosfero-design-toggler', |
5 | 5 | templateUrl: 'app/admin/designModeToggler.html' |
6 | 6 | }) |
7 | -@Inject(DesignModeService, '$scope') | |
7 | +@Inject(DesignModeService) | |
8 | 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 | 15 | private _inDesignMode: boolean = false; |
... | ... | @@ -20,5 +21,4 @@ export class DesignModeTogglerComponent { |
20 | 21 | set inDesignMode(value: boolean) { |
21 | 22 | this.designModeService.setInDesignMode(value); |
22 | 23 | }; |
23 | - | |
24 | 24 | } |
25 | 25 | \ No newline at end of file | ... | ... |
src/app/admin/designModeToggler.html
... | ... | @@ -3,5 +3,6 @@ |
3 | 3 | ng-model="ctrl.inDesignMode" |
4 | 4 | on-label="{{'designMode.toggle.ON' | translate}}" |
5 | 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 | 8 | </toggle-switch> |
8 | 9 | \ No newline at end of file | ... | ... |
src/app/layout/scss/skins/_whbl.scss
... | ... | @@ -289,6 +289,31 @@ $whbl-font-color: #16191c; |
289 | 289 | .pace .pace-progress { |
290 | 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 | 318 | .rtl.skin-whbl #content-wrapper { |
294 | 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 | 4 | import {AuthEvents} from "./../../login/auth-events"; |
5 | 5 | |
6 | 6 | import {EventEmitter} from 'ng-forward'; |
7 | - | |
7 | +import {DesignModeService} from './../../admin/designMode.service'; | |
8 | 8 | |
9 | 9 | describe("BodyStateClasses Service", () => { |
10 | 10 | |
... | ... | @@ -19,10 +19,13 @@ describe("BodyStateClasses Service", () => { |
19 | 19 | }, |
20 | 20 | authService: any = helpers.mocks.authService, |
21 | 21 | bodyEl: { className: string }, |
22 | - bodyElJq: any; | |
22 | + bodyElJq: any, | |
23 | + designModeService = new DesignModeService(); | |
24 | + | |
25 | + | |
23 | 26 | |
24 | 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 | 31 | beforeEach(() => { |
... | ... | @@ -168,4 +171,30 @@ describe("BodyStateClasses Service", () => { |
168 | 171 | |
169 | 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 @@ |
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 | 52 | \ No newline at end of file | ... | ... |