Commit 12f1379fee7191aa0a0289d4cbfadfa8592e9b77
Exists in
master
and in
26 other branches
Merge branch 'comment-paragraph' into 'master'
Frontend for comment paragraph plugin See merge request !7
Showing
52 changed files
with
1055 additions
and
78 deletions
Show diff stats
bower.json
... | ... | @@ -35,6 +35,8 @@ |
35 | 35 | "angular-i18n": "^1.5.0", |
36 | 36 | "angular-load": "^0.4.1", |
37 | 37 | "angular-translate-interpolation-messageformat": "^2.10.0", |
38 | + "angular-bind-html-compile": "^1.2.1", | |
39 | + "angular-click-outside": "^2.7.1", | |
38 | 40 | "ng-ckeditor": "^0.2.1", |
39 | 41 | "ckeditor": "^4.5.8" |
40 | 42 | }, | ... | ... |
gulp/build.js
... | ... | @@ -6,6 +6,7 @@ var rename = require('gulp-rename'); |
6 | 6 | var insert = require('gulp-insert'); |
7 | 7 | var merge = require('merge-stream'); |
8 | 8 | var conf = require('./conf'); |
9 | +var languages = require('./languages'); | |
9 | 10 | |
10 | 11 | var themeName = conf.paths.theme.replace('-', ' '); |
11 | 12 | themeName = themeName.charAt(0).toUpperCase() + themeName.slice(1); |
... | ... | @@ -16,25 +17,31 @@ var $ = require('gulp-load-plugins')({ |
16 | 17 | }); |
17 | 18 | |
18 | 19 | gulp.task('partials', function () { |
19 | - var srcPaths = [path.join(conf.paths.tmp, '/serve/app/**/*.html')]; | |
20 | - conf.paths.allSources.forEach(function(src) { | |
21 | - srcPaths.push(path.join(src, '/app/**/*.html')); | |
20 | + var merged = merge(); | |
21 | + ['app', conf.paths.plugins].forEach(function(partialPath) { | |
22 | + var srcPaths = [path.join(conf.paths.tmp, '/serve/app/**/*.html')]; | |
23 | + conf.paths.allSources.forEach(function(src) { | |
24 | + srcPaths.push(path.join(src, partialPath, '/**/*.html')); | |
25 | + }); | |
26 | + merged.add(gulp.src(srcPaths) | |
27 | + .pipe($.minifyHtml({ | |
28 | + empty: true, | |
29 | + spare: true, | |
30 | + quotes: true | |
31 | + })) | |
32 | + .pipe($.angularTemplatecache('templateCacheHtml-'+partialPath+'.js', { | |
33 | + module: 'noosferoApp', | |
34 | + root: partialPath | |
35 | + })) | |
36 | + .pipe(gulp.dest(conf.paths.tmp + '/partials/'))); | |
22 | 37 | }); |
23 | - return gulp.src(srcPaths) | |
24 | - .pipe($.minifyHtml({ | |
25 | - empty: true, | |
26 | - spare: true, | |
27 | - quotes: true | |
28 | - })) | |
29 | - .pipe($.angularTemplatecache('templateCacheHtml.js', { | |
30 | - module: 'noosferoApp', | |
31 | - root: 'app' | |
32 | - })) | |
33 | - .pipe(gulp.dest(conf.paths.tmp + '/partials/')); | |
38 | + return merged; | |
34 | 39 | }); |
35 | 40 | |
36 | 41 | gulp.task('html', ['inject', 'partials'], function () { |
37 | - var partialsInjectFile = gulp.src(path.join(conf.paths.tmp, '/partials/templateCacheHtml.js'), { read: false }); | |
42 | + var partialsInjectFile = gulp.src([ | |
43 | + path.join(conf.paths.tmp, '/partials/templateCacheHtml-app.js'), | |
44 | + path.join(conf.paths.tmp, '/partials/templateCacheHtml-plugins.js')], { read: false }); | |
38 | 45 | var partialsInjectOptions = { |
39 | 46 | starttag: '<!-- inject:partials -->', |
40 | 47 | ignorePath: path.join(conf.paths.tmp, '/partials'), |
... | ... | @@ -124,6 +131,10 @@ gulp.task('clean-docs', [], function() { |
124 | 131 | return $.del([path.join(conf.paths.docs, '/')]); |
125 | 132 | }); |
126 | 133 | |
134 | +gulp.task('plugin-languages', ['locale'], function() { | |
135 | + return languages.pluginLanguages(conf.paths.dist); | |
136 | +}); | |
137 | + | |
127 | 138 | gulp.task('noosfero', ['html'], function () { |
128 | 139 | var layouts = gulp.src('layouts/**/*') |
129 | 140 | .pipe(gulp.dest(path.join(conf.paths.dist, "layouts"))); |
... | ... | @@ -136,4 +147,4 @@ gulp.task('noosfero', ['html'], function () { |
136 | 147 | return merge(layouts, theme, index); |
137 | 148 | }); |
138 | 149 | |
139 | -gulp.task('build', ['html', 'fonts', 'other', 'locale', 'noosfero']); | |
150 | +gulp.task('build', ['html', 'fonts', 'other', 'locale', 'plugin-languages', 'noosfero']); | ... | ... |
gulp/conf.js
... | ... | @@ -15,11 +15,13 @@ var path = require('path'); |
15 | 15 | */ |
16 | 16 | exports.paths = { |
17 | 17 | src: 'src', |
18 | + plugins: 'plugins', | |
18 | 19 | dist: 'dist', |
19 | 20 | tmp: '.tmp', |
20 | 21 | e2e: 'e2e', |
21 | 22 | docs: 'docs', |
22 | - themes: 'themes' | |
23 | + themes: 'themes', | |
24 | + languages: 'languages' | |
23 | 25 | }; |
24 | 26 | exports.configTheme = function(theme) { |
25 | 27 | exports.paths.theme = theme || "angular-default"; | ... | ... |
... | ... | @@ -0,0 +1,29 @@ |
1 | +'use strict'; | |
2 | + | |
3 | +var path = require('path'); | |
4 | +var gulp = require('gulp'); | |
5 | +var merge = require('merge-stream'); | |
6 | +var conf = require('./conf'); | |
7 | +var mergeJson = require('gulp-merge-json'); | |
8 | +var glob = require("glob"); | |
9 | + | |
10 | +exports.pluginLanguages = function(dest) { | |
11 | + var merged = merge(); | |
12 | + glob(path.join(conf.paths.src, conf.paths.languages, "*.json"), function (er, files) { | |
13 | + files.forEach(function(file) { | |
14 | + merged.add(exports.pluginLanguage(file, dest)); | |
15 | + }); | |
16 | + }); | |
17 | + return merged; | |
18 | +} | |
19 | + | |
20 | +exports.pluginLanguage = function(file, dest) { | |
21 | + var language = file.split('/').pop().replace('\.json',''); | |
22 | + return gulp.src(path.join(conf.paths.src, '**', conf.paths.languages, language+'.json')) | |
23 | + .pipe(mergeJson(path.join(conf.paths.languages, language+'.json'))) | |
24 | + .pipe(gulp.dest(dest)) | |
25 | +} | |
26 | + | |
27 | +gulp.task('serve-languages', function() { | |
28 | + return exports.pluginLanguages(path.join(conf.paths.tmp, '/serve')); | |
29 | +}); | ... | ... |
gulp/server.js
... | ... | @@ -45,7 +45,7 @@ browserSync.use(browserSyncSpa({ |
45 | 45 | selector: '[ng-app]'// Only needed for angular apps |
46 | 46 | })); |
47 | 47 | |
48 | -gulp.task('serve', ['watch'], function () { | |
48 | +gulp.task('serve', ['serve-languages', 'watch'], function () { | |
49 | 49 | var srcPaths = [path.join(conf.paths.tmp, '/serve')]; |
50 | 50 | conf.paths.allSources.reverse().forEach(function(src) { |
51 | 51 | srcPaths.push(src); | ... | ... |
gulp/styles.js
... | ... | @@ -31,6 +31,7 @@ var buildStyles = function() { |
31 | 31 | ]; |
32 | 32 | conf.paths.allSources.forEach(function(src) { |
33 | 33 | srcPaths.push(path.join(src, '/app/**/*.scss')); |
34 | + srcPaths.push(path.join(src, conf.paths.plugins, '/**/*.scss')); | |
34 | 35 | }); |
35 | 36 | var injectFiles = gulp.src(srcPaths, { read: false }); |
36 | 37 | ... | ... |
gulp/watch.js
... | ... | @@ -3,6 +3,7 @@ |
3 | 3 | var path = require('path'); |
4 | 4 | var gulp = require('gulp'); |
5 | 5 | var conf = require('./conf'); |
6 | +var languages = require('./languages'); | |
6 | 7 | |
7 | 8 | var browserSync = require('browser-sync'); |
8 | 9 | |
... | ... | @@ -14,13 +15,11 @@ gulp.task('watch', ['inject'], function () { |
14 | 15 | |
15 | 16 | gulp.watch([path.join(conf.paths.src, '/*.html'), 'bower.json'], ['inject-reload']); |
16 | 17 | |
17 | - var stylePaths = []; | |
18 | - conf.paths.allSources.forEach(function(src) { | |
19 | - stylePaths.push(path.join(src, '/app/**/*.css')); | |
20 | - stylePaths.push(path.join(src, '/app/**/*.scss')); | |
21 | - }); | |
22 | - | |
23 | - gulp.watch(stylePaths, function(event) { | |
18 | + gulp.watch([ | |
19 | + path.join(conf.paths.src, '/app/**/*.css'), | |
20 | + path.join(conf.paths.src, '/app/**/*.scss'), | |
21 | + path.join(conf.paths.src, conf.paths.plugins, '/**/*.scss') | |
22 | + ], function(event) { | |
24 | 23 | if(isOnlyChange(event)) { |
25 | 24 | gulp.start('styles-reload'); |
26 | 25 | } else { |
... | ... | @@ -36,9 +35,14 @@ gulp.task('watch', ['inject'], function () { |
36 | 35 | } |
37 | 36 | }); |
38 | 37 | |
38 | + gulp.watch(path.join(conf.paths.src, '**', conf.paths.languages, '*.json'), function(event) { | |
39 | + languages.pluginLanguage(event.path, path.join(conf.paths.tmp, '/serve')); | |
40 | + }); | |
41 | + | |
39 | 42 | var watchPaths = []; |
40 | 43 | conf.paths.allSources.forEach(function(src) { |
41 | 44 | watchPaths.push(path.join(src, '/app/**/*.html')); |
45 | + watchPaths.push(path.join(src, conf.paths.plugins, '/**/*.html')); | |
42 | 46 | }); |
43 | 47 | gulp.watch(watchPaths, function(event) { |
44 | 48 | browserSync.reload(event.path); | ... | ... |
karma.conf.js
package.json
src/app/article/article-default-view.component.ts
1 | 1 | import { bundle, Input, Inject, Component, Directive } from 'ng-forward'; |
2 | 2 | import {ArticleBlogComponent} from "./types/blog/blog.component"; |
3 | 3 | import {CommentsComponent} from "./comment/comments.component"; |
4 | +import {MacroDirective} from "./macro/macro.directive"; | |
5 | +import {ArticleToolbarHotspotComponent} from "../hotspot/article-toolbar-hotspot.component"; | |
4 | 6 | |
5 | 7 | /** |
6 | 8 | * @ngdoc controller |
... | ... | @@ -30,7 +32,8 @@ export class ArticleDefaultViewComponent { |
30 | 32 | @Component({ |
31 | 33 | selector: 'noosfero-article', |
32 | 34 | template: 'not-used', |
33 | - directives: [ArticleDefaultViewComponent, ArticleBlogComponent, CommentsComponent] | |
35 | + directives: [ArticleDefaultViewComponent, ArticleBlogComponent, | |
36 | + CommentsComponent, MacroDirective, ArticleToolbarHotspotComponent] | |
34 | 37 | }) |
35 | 38 | @Inject("$element", "$scope", "$injector", "$compile") |
36 | 39 | export class ArticleViewComponent { |
... | ... | @@ -53,6 +56,5 @@ export class ArticleViewComponent { |
53 | 56 | private $scope: ng.IScope, |
54 | 57 | private $injector: ng.auto.IInjectorService, |
55 | 58 | private $compile: ng.ICompileService) { |
56 | - | |
57 | 59 | } |
58 | 60 | } | ... | ... |
src/app/article/article.html
... | ... | @@ -4,9 +4,7 @@ |
4 | 4 | </div> |
5 | 5 | |
6 | 6 | <div class="sub-header clearfix"> |
7 | - <a href="#" class="btn btn-default btn-xs" ui-sref="main.cmsEdit({profile: ctrl.profile.identifier, id: ctrl.article.id})"> | |
8 | - <i class="fa fa-pencil-square-o fa-fw fa-lg"></i> {{"article.actions.edit" | translate}} | |
9 | - </a> | |
7 | + <noosfero-hotspot-article-toolbar [article]="ctrl.article"></noosfero-hotspot-article-toolbar> | |
10 | 8 | <div class="page-info pull-right small text-muted"> |
11 | 9 | <span class="time"> |
12 | 10 | <i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.article.created_at | dateFormat"></span> |
... | ... | @@ -21,7 +19,7 @@ |
21 | 19 | </div> |
22 | 20 | |
23 | 21 | <div class="page-body"> |
24 | - <div ng-bind-html="ctrl.article.body"></div> | |
22 | + <div bind-html-compile="ctrl.article.body"></div> | |
25 | 23 | </div> |
26 | 24 | |
27 | 25 | <noosfero-comments [article]="ctrl.article"></noosfero-comments> | ... | ... |
src/app/article/comment/comment.component.ts
... | ... | @@ -9,6 +9,8 @@ export class CommentComponent { |
9 | 9 | |
10 | 10 | @Input() comment: noosfero.CommentViewModel; |
11 | 11 | @Input() article: noosfero.Article; |
12 | + @Input() displayActions = true; | |
13 | + @Input() displayReplies = true; | |
12 | 14 | |
13 | 15 | showReply() { |
14 | 16 | return this.comment && this.comment.__show_reply === true; | ... | ... |
src/app/article/comment/comment.html
... | ... | @@ -9,13 +9,19 @@ |
9 | 9 | <a class="pull-left" ui-sref="main.profile.home({profile: ctrl.comment.author.identifier})"> |
10 | 10 | <h4 class="media-heading">{{ctrl.comment.author.name}}</h4> |
11 | 11 | </a> |
12 | + <span class="reply-of" ng-if="ctrl.comment.reply_of" uib-tooltip-template="'app/article/comment/comment-reply-tooltip.html'"> | |
13 | + <i class="fa fa-fw fa-mail-forward"></i> | |
14 | + <span class="author">{{ctrl.comment.reply_of.author.name}}</span> | |
15 | + </span> | |
12 | 16 | <span class="date" am-time-ago="ctrl.comment.created_at | dateFormat"></span> |
13 | 17 | </div> |
14 | 18 | <div class="title">{{ctrl.comment.title}}</div> |
15 | 19 | <div class="body">{{ctrl.comment.body}}</div> |
16 | - <a href="#" (click)="ctrl.reply()" class="small text-muted"> | |
17 | - {{"comment.reply" | translate}} | |
18 | - </a> | |
20 | + <div class="actions" ng-if="ctrl.displayActions"> | |
21 | + <a href="#" (click)="ctrl.reply()" class="small text-muted"> | |
22 | + {{"comment.reply" | translate}} | |
23 | + </a> | |
24 | + </div> | |
19 | 25 | </div> |
20 | - <noosfero-comments [show-form]="ctrl.showReply()" [article]="ctrl.article" [parent]="ctrl.comment"></noosfero-comments> | |
26 | + <noosfero-comments [show-form]="ctrl.showReply" [article]="ctrl.article" [parent]="ctrl.comment" ng-if="ctrl.displayReplies"></noosfero-comments> | |
21 | 27 | </div> | ... | ... |
src/app/article/comment/comment.scss
... | ... | @@ -5,6 +5,7 @@ |
5 | 5 | @extend .text-muted; |
6 | 6 | @extend .small; |
7 | 7 | margin-left: 8px; |
8 | + font-size: 12px; | |
8 | 9 | } |
9 | 10 | .title { |
10 | 11 | font-weight: bold; |
... | ... | @@ -13,7 +14,18 @@ |
13 | 14 | min-width: 40px; |
14 | 15 | } |
15 | 16 | .media-body { |
16 | - padding: 0 10px 10px 10px; | |
17 | + padding: 0 10px 10px 0; | |
18 | + .reply-of { | |
19 | + font-size: 12px; | |
20 | + color: #B5B5B5; | |
21 | + margin-left: 5px; | |
22 | + i { | |
23 | + font-size: 10px; | |
24 | + } | |
25 | + } | |
26 | + h4 { | |
27 | + font-size: 16px; | |
28 | + } | |
17 | 29 | } |
18 | 30 | noosfero-profile-image { |
19 | 31 | img { |
... | ... | @@ -27,8 +39,24 @@ |
27 | 39 | font-size: 1.7em; |
28 | 40 | } |
29 | 41 | } |
42 | + // Limit identation of replies | |
30 | 43 | .comments { |
31 | 44 | margin-left: 30px; |
45 | + .comments .comments { | |
46 | + margin-left: 0px; | |
47 | + .comment { | |
48 | + margin-left: 0px; | |
49 | + } | |
50 | + } | |
51 | + } | |
52 | + .tooltip-inner { | |
53 | + max-width: 350px; | |
54 | + text-align: left; | |
55 | + .reply-tooltip { | |
56 | + .comment { | |
57 | + margin: 5px; | |
58 | + } | |
59 | + } | |
32 | 60 | } |
33 | 61 | } |
34 | 62 | } | ... | ... |
src/app/article/comment/comments.component.ts
1 | -import { Inject, Input, Output, Component, provide, EventEmitter } from 'ng-forward'; | |
2 | -import {INgForwardJQuery} from "ng-forward/cjs/util/jqlite-extensions"; | |
3 | - | |
4 | - | |
1 | +import { Inject, Input, Component, provide } from 'ng-forward'; | |
5 | 2 | import { PostCommentComponent } from "./post-comment/post-comment.component"; |
6 | 3 | import { CommentService } from "../../../lib/ng-noosfero-api/http/comment.service"; |
7 | 4 | import { CommentComponent } from "./comment.component"; |
5 | +import { PostCommentEventService } from "./post-comment/post-comment-event.service"; | |
8 | 6 | |
9 | 7 | @Component({ |
10 | 8 | selector: 'noosfero-comments', |
11 | 9 | templateUrl: 'app/article/comment/comments.html', |
12 | - directives: [PostCommentComponent, CommentComponent], | |
13 | - outputs: ['commentAdded'] | |
10 | + directives: [PostCommentComponent, CommentComponent] | |
14 | 11 | }) |
15 | -@Inject(CommentService, "$element") | |
12 | +@Inject(CommentService, PostCommentEventService, "$scope") | |
16 | 13 | export class CommentsComponent { |
17 | 14 | |
18 | - comments: noosfero.CommentViewModel[] = []; | |
15 | + comments: noosfero.Comment[] = []; | |
19 | 16 | @Input() showForm = true; |
20 | 17 | @Input() article: noosfero.Article; |
21 | - @Input() parent: noosfero.CommentViewModel; | |
22 | - | |
18 | + @Input() parent: noosfero.Comment; | |
23 | 19 | protected page = 1; |
24 | 20 | protected perPage = 5; |
25 | 21 | protected total = 0; |
26 | 22 | |
27 | - constructor(protected commentService: CommentService) { } | |
23 | + newComment = <noosfero.Comment>{}; | |
24 | + | |
25 | + constructor(protected commentService: CommentService, private postCommentEventService: PostCommentEventService, private $scope: ng.IScope) { } | |
28 | 26 | |
29 | 27 | ngOnInit() { |
30 | 28 | if (this.parent) { |
... | ... | @@ -32,20 +30,13 @@ export class CommentsComponent { |
32 | 30 | } else { |
33 | 31 | this.loadNextPage(); |
34 | 32 | } |
35 | - } | |
36 | - | |
37 | - commentAdded(comment: noosfero.Comment): void { | |
38 | - this.comments.push(comment); | |
39 | - this.resetShowReply(); | |
40 | - } | |
41 | - | |
42 | - private resetShowReply() { | |
43 | - this.comments.forEach((comment: noosfero.CommentViewModel) => { | |
44 | - comment.__show_reply = false; | |
33 | + this.postCommentEventService.subscribe((comment: noosfero.Comment) => { | |
34 | + if ((!this.parent && !comment.reply_of) || (comment.reply_of && this.parent && comment.reply_of.id === this.parent.id)) { | |
35 | + if (!this.comments) this.comments = []; | |
36 | + this.comments.push(comment); | |
37 | + this.$scope.$apply(); | |
38 | + } | |
45 | 39 | }); |
46 | - if (this.parent) { | |
47 | - this.parent.__show_reply = false; | |
48 | - } | |
49 | 40 | } |
50 | 41 | |
51 | 42 | loadComments() { | ... | ... |
src/app/article/comment/comments.html
1 | 1 | <div class="comments"> |
2 | - <noosfero-post-comment (comment-saved)="ctrl.commentAdded($event.detail)" ng-if="ctrl.showForm" [article]="ctrl.article" [parent]="ctrl.parent"></noosfero-post-comment> | |
2 | + <noosfero-post-comment ng-if="ctrl.showForm" [article]="ctrl.article" [parent]="ctrl.parent" [comment]="ctrl.newComment"></noosfero-post-comment> | |
3 | 3 | |
4 | 4 | <div class="comments-list"> |
5 | 5 | <noosfero-comment ng-repeat="comment in ctrl.comments | orderBy: 'created_at':true" [comment]="comment" [article]="ctrl.article"></noosfero-comment> | ... | ... |
src/app/article/comment/post-comment/post-comment.component.ts
1 | -import { Inject, Input, Output, EventEmitter, Component } from 'ng-forward'; | |
1 | +import { Inject, Input, Component } from 'ng-forward'; | |
2 | 2 | import { CommentService } from "../../../../lib/ng-noosfero-api/http/comment.service"; |
3 | 3 | import { NotificationService } from "../../../shared/services/notification.service"; |
4 | 4 | import { SessionService } from "../../../login"; |
5 | +import { PostCommentEventService } from "./post-comment-event.service"; | |
6 | +import { CommentFormHotspotComponent } from "../../../hotspot/comment-form-hotspot.component"; | |
5 | 7 | |
6 | 8 | @Component({ |
7 | 9 | selector: 'noosfero-post-comment', |
8 | 10 | templateUrl: 'app/article/comment/post-comment/post-comment.html', |
9 | - outputs: ['commentSaved'] | |
11 | + directives: [CommentFormHotspotComponent] | |
10 | 12 | }) |
11 | -@Inject(CommentService, NotificationService, SessionService) | |
13 | +@Inject(CommentService, NotificationService, SessionService, PostCommentEventService) | |
12 | 14 | export class PostCommentComponent { |
13 | 15 | |
16 | + public static EVENT_COMMENT_RECEIVED = "comment.received"; | |
17 | + | |
14 | 18 | @Input() article: noosfero.Article; |
15 | 19 | @Input() parent: noosfero.Comment; |
16 | - @Output() commentSaved: EventEmitter<Comment> = new EventEmitter<Comment>(); | |
17 | - | |
18 | - comment = <noosfero.Comment>{}; | |
20 | + @Input() comment = <noosfero.Comment>{}; | |
19 | 21 | private currentUser: noosfero.User; |
20 | 22 | |
21 | 23 | constructor(private commentService: CommentService, |
22 | 24 | private notificationService: NotificationService, |
23 | - private session: SessionService) { | |
25 | + private session: SessionService, | |
26 | + private postCommentEventService: PostCommentEventService) { | |
24 | 27 | this.currentUser = this.session.currentUser(); |
25 | 28 | } |
26 | 29 | |
... | ... | @@ -29,7 +32,7 @@ export class PostCommentComponent { |
29 | 32 | this.comment.reply_of_id = this.parent.id; |
30 | 33 | } |
31 | 34 | this.commentService.createInArticle(this.article, this.comment).then((result: noosfero.RestResult<noosfero.Comment>) => { |
32 | - this.commentSaved.next(result.data); | |
35 | + this.postCommentEventService.emit(result.data); | |
33 | 36 | this.comment.body = ""; |
34 | 37 | this.notificationService.success({ title: "comment.post.success.title", message: "comment.post.success.message" }); |
35 | 38 | }); | ... | ... |
src/app/article/comment/post-comment/post-comment.html
... | ... | @@ -8,6 +8,7 @@ |
8 | 8 | </div> |
9 | 9 | <div class="media-body"> |
10 | 10 | <textarea class="form-control custom-control" rows="1" ng-model="ctrl.comment.body" placeholder="{{'comment.post.placeholder' | translate}}"></textarea> |
11 | + <noosfero-hotspot-comment-form [comment]="ctrl.comment" [parent]="ctrl.parent"></noosfero-hotspot-comment-form> | |
11 | 12 | <button ng-show="ctrl.comment.body" type="submit" class="btn btn-default pull-right ng-hide" ng-click="ctrl.save()">{{"comment.post" | translate}}</button> |
12 | 13 | </div> |
13 | 14 | </div> | ... | ... |
... | ... | @@ -0,0 +1,25 @@ |
1 | +import {Input, provide, Component} from 'ng-forward'; | |
2 | +import {MacroDirective} from "./macro.directive"; | |
3 | + | |
4 | +import * as helpers from "../../../spec/helpers"; | |
5 | + | |
6 | +const htmlTemplate: string = '<div data-macro="macro_component" data-macro-custom="custom"></div>'; | |
7 | + | |
8 | +describe("Directives", () => { | |
9 | + | |
10 | + describe("Macro directive", () => { | |
11 | + it("renders a macro component using the name passed in data-macro", (done: Function) => { | |
12 | + helpers.quickCreateComponent({ template: htmlTemplate, directives: [MacroDirective] }).then((fixture) => { | |
13 | + expect(fixture.debugElement.queryAll('macro-component').length).toEqual(1); | |
14 | + done(); | |
15 | + }); | |
16 | + }); | |
17 | + | |
18 | + it("extract custom attributes from macro", (done: Function) => { | |
19 | + helpers.quickCreateComponent({ template: htmlTemplate, directives: [MacroDirective] }).then((fixture) => { | |
20 | + expect(fixture.debugElement.query('macro-component').attr("custom")).toEqual("custom"); | |
21 | + done(); | |
22 | + }); | |
23 | + }); | |
24 | + }); | |
25 | +}); | ... | ... |
... | ... | @@ -0,0 +1,34 @@ |
1 | +import {Directive, Inject} from "ng-forward"; | |
2 | + | |
3 | +@Directive({ | |
4 | + selector: '[macro]', | |
5 | + providers: [] | |
6 | +}) | |
7 | +@Inject('$element', '$scope', '$compile') | |
8 | +export class MacroDirective { | |
9 | + | |
10 | + private macroPrefix = "data-macro"; | |
11 | + | |
12 | + constructor(private $element: any, private $scope: ng.IScope, private $compile: ng.ICompileService) { | |
13 | + let macro = $element[0].attributes[this.macroPrefix].value; | |
14 | + let componentName = this.normalizeName(macro); | |
15 | + let content = $element.html().replace(/"/g, '"'); | |
16 | + let customAttributes = this.extractCustomAttributes($element[0].attributes); | |
17 | + $element.replaceWith($compile(`<${componentName} [article]="ctrl.article" content="${content}" ${customAttributes}></${componentName}>`)($scope)); | |
18 | + } | |
19 | + | |
20 | + extractCustomAttributes(attributes: any) { | |
21 | + let customAttributes = ""; | |
22 | + for (let attr of attributes) { | |
23 | + if (attr.name.startsWith(this.macroPrefix + '-')) { | |
24 | + let name = this.normalizeName(attr.name.replace(this.macroPrefix + '-', '')); | |
25 | + customAttributes += ` ${name}='${attr.value}'`; | |
26 | + } | |
27 | + } | |
28 | + return customAttributes; | |
29 | + } | |
30 | + | |
31 | + normalizeName(name: string) { | |
32 | + return name.replace(/[_\/]/g, '-').toLowerCase(); | |
33 | + } | |
34 | +} | ... | ... |
... | ... | @@ -0,0 +1,25 @@ |
1 | +import {Component, Input, Inject} from "ng-forward"; | |
2 | +import * as plugins from "../../plugins"; | |
3 | +import {dasherize} from "ng-forward/cjs/util/helpers"; | |
4 | +import {PluginHotspot} from "./plugin-hotspot"; | |
5 | + | |
6 | +@Component({ | |
7 | + selector: "noosfero-hotspot-article-toolbar", | |
8 | + template: "<span></span>" | |
9 | +}) | |
10 | +@Inject("$element", "$scope", "$compile") | |
11 | +export class ArticleToolbarHotspotComponent extends PluginHotspot { | |
12 | + | |
13 | + @Input() article: noosfero.Article; | |
14 | + | |
15 | + constructor( | |
16 | + private $element: any, | |
17 | + private $scope: ng.IScope, | |
18 | + private $compile: ng.ICompileService) { | |
19 | + super("article_extra_toolbar_buttons"); | |
20 | + } | |
21 | + | |
22 | + addHotspot(directiveName: string) { | |
23 | + this.$element.append(this.$compile('<' + directiveName + ' [article]="ctrl.article"></' + directiveName + '>')(this.$scope)); | |
24 | + } | |
25 | +} | ... | ... |
... | ... | @@ -0,0 +1,26 @@ |
1 | +import {Component, Input, Inject} from "ng-forward"; | |
2 | +import * as plugins from "../../plugins"; | |
3 | +import {dasherize} from "ng-forward/cjs/util/helpers"; | |
4 | +import {PluginHotspot} from "./plugin-hotspot"; | |
5 | + | |
6 | +@Component({ | |
7 | + selector: "noosfero-hotspot-comment-form", | |
8 | + template: "<span></span>" | |
9 | +}) | |
10 | +@Inject("$element", "$scope", "$compile") | |
11 | +export class CommentFormHotspotComponent extends PluginHotspot { | |
12 | + | |
13 | + @Input() comment: noosfero.Comment; | |
14 | + @Input() parent: noosfero.Comment; | |
15 | + | |
16 | + constructor( | |
17 | + private $element: any, | |
18 | + private $scope: ng.IScope, | |
19 | + private $compile: ng.ICompileService) { | |
20 | + super("comment_form_extra_contents"); | |
21 | + } | |
22 | + | |
23 | + addHotspot(directiveName: string) { | |
24 | + this.$element.append(this.$compile('<' + directiveName + ' [comment]="ctrl.comment" [parent]="ctrl.parent"></' + directiveName + '>')(this.$scope)); | |
25 | + } | |
26 | +} | ... | ... |
... | ... | @@ -0,0 +1,19 @@ |
1 | +import {Component, Input, Inject} from "ng-forward"; | |
2 | +import * as plugins from "../../plugins"; | |
3 | +import {dasherize} from "ng-forward/cjs/util/helpers"; | |
4 | + | |
5 | +export abstract class PluginHotspot { | |
6 | + | |
7 | + constructor(protected hotspot: string) { } | |
8 | + | |
9 | + ngOnInit() { | |
10 | + for (let component of plugins.hotspots) { | |
11 | + if (component.hotspot === this.hotspot) { | |
12 | + let directiveName = dasherize(component.name.replace('Component', '')); | |
13 | + this.addHotspot(directiveName); | |
14 | + } | |
15 | + } | |
16 | + } | |
17 | + | |
18 | + abstract addHotspot(directiveName: string): any; | |
19 | +} | ... | ... |
src/app/index.ts
... | ... | @@ -15,9 +15,10 @@ declare var moment: any; |
15 | 15 | let noosferoApp: any = bundle("noosferoApp", MainComponent, ["ngAnimate", "ngCookies", "ngStorage", "ngTouch", |
16 | 16 | "ngSanitize", "ngMessages", "ngAria", "restangular", |
17 | 17 | "ui.router", "ui.bootstrap", "toastr", "ngCkeditor", |
18 | - "angularMoment", "angular.filter", "akoenig.deckgrid", | |
18 | + "angular-bind-html-compile","angularMoment", "angular.filter", "akoenig.deckgrid", | |
19 | 19 | "angular-timeline", "duScroll", "oitozero.ngSweetAlert", |
20 | - "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad"]).publish(); | |
20 | + "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad", | |
21 | + "angular-click-outside"]).publish(); | |
21 | 22 | |
22 | 23 | NoosferoApp.angularModule = noosferoApp; |
23 | 24 | ... | ... |
src/app/main/main.component.ts
1 | +import * as plugins from "../../plugins"; | |
1 | 2 | import {bundle, Component, StateConfig, Inject} from "ng-forward"; |
2 | 3 | import {ArticleBlogComponent} from "./../article/types/blog/blog.component"; |
3 | 4 | |
... | ... | @@ -93,7 +94,7 @@ export class EnvironmentContent { |
93 | 94 | LinkListBlockComponent, CommunitiesBlockComponent, HtmlEditorComponent, |
94 | 95 | MainBlockComponent, RecentDocumentsBlockComponent, Navbar, SidebarComponent, ProfileImageBlockComponent, |
95 | 96 | MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent |
96 | - ], | |
97 | + ].concat(plugins.mainComponents).concat(plugins.hotspots), | |
97 | 98 | providers: [AuthService, SessionService, NotificationService, BodyStateClassesService] |
98 | 99 | }) |
99 | 100 | @StateConfig([ | ... | ... |
src/lib/ng-noosfero-api/http/restangular_service.spec.ts
... | ... | @@ -207,4 +207,27 @@ describe("Restangular Service - base Class", () => { |
207 | 207 | $httpBackend.flush(); |
208 | 208 | }); |
209 | 209 | |
210 | -}); | |
211 | 210 | \ No newline at end of file |
211 | + it("post('customPath', rootObject) calls POST /rootObjects/1/customPath", (done) => { | |
212 | + let rootObj: RootObjectModel = rootObjectRestService.getElement(1); | |
213 | + $httpBackend.expectPOST("/api/v1/rootObjects/1/customPath").respond(201, { object: { attr: 1, rootId: 1 } }); | |
214 | + objectRestService.post('customPath', rootObj).then((result: noosfero.RestResult<ObjectModel>) => { | |
215 | + expect(result.data).toBeDefined(); | |
216 | + expect((<any>result.data).attr).toEqual(1); | |
217 | + expect((<any>result.data).rootId).toEqual(1); | |
218 | + done(); | |
219 | + }); | |
220 | + $httpBackend.flush(); | |
221 | + }); | |
222 | + | |
223 | + it("post('customPath') calls POST /objects/customPath", (done) => { | |
224 | + $httpBackend.expectPOST("/api/v1/objects/customPath").respond(201, { object: { attr: 1, rootId: 1 } }); | |
225 | + objectRestService.post('customPath').then((result: noosfero.RestResult<ObjectModel>) => { | |
226 | + expect(result.data).toBeDefined(); | |
227 | + expect((<any>result.data).attr).toEqual(1); | |
228 | + expect((<any>result.data).rootId).toEqual(1); | |
229 | + done(); | |
230 | + }); | |
231 | + $httpBackend.flush(); | |
232 | + }); | |
233 | + | |
234 | +}); | ... | ... |
src/lib/ng-noosfero-api/http/restangular_service.ts
... | ... | @@ -261,6 +261,22 @@ export abstract class RestangularService<T extends noosfero.RestModel> { |
261 | 261 | return deferred.promise; |
262 | 262 | } |
263 | 263 | |
264 | + | |
265 | + public post(path: string, rootElement?: restangular.IElement, data?: any, headers?: any): ng.IPromise<noosfero.RestResult<T>> { | |
266 | + let deferred = this.$q.defer<noosfero.RestResult<T>>(); | |
267 | + let restRequest: ng.IPromise<any>; | |
268 | + | |
269 | + if (rootElement) { | |
270 | + restRequest = rootElement.customPOST(data, path, headers); | |
271 | + } else { | |
272 | + restRequest = this.baseResource.customPOST(data, path, headers); | |
273 | + } | |
274 | + restRequest | |
275 | + .then(this.getHandleSuccessFunction(deferred)) | |
276 | + .catch(this.getHandleErrorFunction(deferred)); | |
277 | + return deferred.promise; | |
278 | + } | |
279 | + | |
264 | 280 | /** |
265 | 281 | * Returns a Restangular IElement representing the |
266 | 282 | */ | ... | ... |
src/lib/ng-noosfero-api/interfaces/article.ts
1 | 1 | |
2 | 2 | namespace noosfero { |
3 | - export interface Article extends RestModel { | |
3 | + export interface Article extends RestModel { | |
4 | 4 | path: string; |
5 | 5 | profile: Profile; |
6 | 6 | type: string; |
7 | - parent: Article; | |
7 | + parent: Article; | |
8 | 8 | body: string; |
9 | 9 | title: string; |
10 | 10 | name: string; |
11 | 11 | published: boolean; |
12 | + setting: any; | |
12 | 13 | } |
13 | 14 | -} |
15 | +} | |
14 | 16 | \ No newline at end of file | ... | ... |
src/plugins/comment_paragraph/allow-comment/allow-comment.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,78 @@ |
1 | +import {AllowCommentComponent} from "./allow-comment.component"; | |
2 | +import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; | |
3 | +import * as helpers from "../../../spec/helpers"; | |
4 | +import {Provider} from 'ng-forward'; | |
5 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
6 | + | |
7 | +let htmlTemplate = '<comment-paragraph-plugin-allow-comment [content]="ctrl.content" [paragraph-uuid]="ctrl.paragraphUuid" [article]="ctrl.article"></comment-paragraph-plugin-allow-comment>'; | |
8 | + | |
9 | +describe("Components", () => { | |
10 | + describe("Allow Comment Component", () => { | |
11 | + | |
12 | + let serviceMock = { | |
13 | + commentParagraphCount: () => { | |
14 | + return Promise.resolve(5); | |
15 | + } | |
16 | + }; | |
17 | + let functionToggleCommentParagraph: Function; | |
18 | + let eventServiceMock = { | |
19 | + // toggleCommentParagraph | |
20 | + subscribeToggleCommentParagraph: (fn: Function) => { | |
21 | + functionToggleCommentParagraph = fn; | |
22 | + } | |
23 | + }; | |
24 | + | |
25 | + let providers = [ | |
26 | + new Provider('CommentParagraphService', { useValue: serviceMock }), | |
27 | + new Provider('CommentParagraphEventService', { useValue: eventServiceMock }) | |
28 | + ]; | |
29 | + let helper: ComponentTestHelper; | |
30 | + | |
31 | + beforeEach(angular.mock.module("templates")); | |
32 | + | |
33 | + beforeEach((done) => { | |
34 | + let cls = createClass({ | |
35 | + template: htmlTemplate, | |
36 | + directives: [AllowCommentComponent], | |
37 | + providers: providers, | |
38 | + properties: { | |
39 | + content: "", | |
40 | + paragraphUuid: "uuid", | |
41 | + article: { | |
42 | + setting: { | |
43 | + comment_paragraph_plugin_activate: true | |
44 | + } | |
45 | + } | |
46 | + } | |
47 | + }); | |
48 | + helper = new ComponentTestHelper(cls, done); | |
49 | + }); | |
50 | + | |
51 | + it('update comments count', () => { | |
52 | + expect(helper.component.commentsCount).toEqual(5); | |
53 | + }); | |
54 | + | |
55 | + it('display paragraph content', () => { | |
56 | + expect(helper.all(".paragraph .paragraph-content").length).toEqual(1); | |
57 | + }); | |
58 | + | |
59 | + it('display button to side comments', () => { | |
60 | + expect(helper.all(".paragraph .actions a").length).toEqual(1); | |
61 | + }); | |
62 | + | |
63 | + it('set display to true when click in show paragraph', () => { | |
64 | + helper.component.showParagraphComments(); | |
65 | + expect(helper.component.display).toBeTruthy(); | |
66 | + }); | |
67 | + | |
68 | + it('set display to false when click in hide paragraph', () => { | |
69 | + helper.component.hideParagraphComments(); | |
70 | + expect(helper.component.display).toBeFalsy(); | |
71 | + }); | |
72 | + | |
73 | + it('update article when receive a toogle paragraph event', () => { | |
74 | + functionToggleCommentParagraph({ id: 2 }); | |
75 | + expect(helper.component.article.id).toEqual(2); | |
76 | + }); | |
77 | + }); | |
78 | +}); | ... | ... |
src/plugins/comment_paragraph/allow-comment/allow-comment.component.ts
0 → 100644
... | ... | @@ -0,0 +1,45 @@ |
1 | +import {Component, Input, Inject} from "ng-forward"; | |
2 | +import {SideCommentsComponent} from "../side-comments/side-comments.component"; | |
3 | +import {CommentParagraphEventService} from "../events/comment-paragraph-event.service"; | |
4 | +import {CommentParagraphService} from "../http/comment-paragraph.service"; | |
5 | + | |
6 | +@Component({ | |
7 | + selector: "comment-paragraph-plugin-allow-comment", | |
8 | + templateUrl: "plugins/comment_paragraph/allow-comment/allow-comment.html", | |
9 | + directives: [SideCommentsComponent] | |
10 | +}) | |
11 | +@Inject("$scope", CommentParagraphEventService, CommentParagraphService) | |
12 | +export class AllowCommentComponent { | |
13 | + | |
14 | + @Input() content: string; | |
15 | + @Input() paragraphUuid: string; | |
16 | + @Input() article: noosfero.Article; | |
17 | + commentsCount: number; | |
18 | + display = false; | |
19 | + | |
20 | + constructor(private $scope: ng.IScope, | |
21 | + private commentParagraphEventService: CommentParagraphEventService, | |
22 | + private commentParagraphService: CommentParagraphService) { } | |
23 | + | |
24 | + ngOnInit() { | |
25 | + this.commentParagraphEventService.subscribeToggleCommentParagraph((article: noosfero.Article) => { | |
26 | + this.article = article; | |
27 | + this.$scope.$apply(); | |
28 | + }); | |
29 | + this.commentParagraphService.commentParagraphCount(this.article, this.paragraphUuid).then((count: number) => { | |
30 | + this.commentsCount = count; | |
31 | + }); | |
32 | + } | |
33 | + | |
34 | + isActivated() { | |
35 | + return this.article && this.article.setting && this.article.setting.comment_paragraph_plugin_activate; | |
36 | + } | |
37 | + | |
38 | + showParagraphComments() { | |
39 | + this.display = true; | |
40 | + } | |
41 | + | |
42 | + hideParagraphComments() { | |
43 | + this.display = false; | |
44 | + } | |
45 | +} | ... | ... |
src/plugins/comment_paragraph/allow-comment/allow-comment.html
0 → 100644
... | ... | @@ -0,0 +1,12 @@ |
1 | +<div class="paragraph" ng-class="{'active' : ctrl.display}"> | |
2 | + <div class="paragraph-content" ng-bind-html="ctrl.content" ng-class="{'active' : ctrl.display}"></div> | |
3 | + <div ng-if="ctrl.isActivated()" class="actions"> | |
4 | + <a href="#" popover-placement="right-top" popover-trigger="none" | |
5 | + uib-popover-template="'plugins/comment_paragraph/allow-comment/popover.html'" | |
6 | + (click)="ctrl.showParagraphComments()" popover-is-open="ctrl.display"> | |
7 | + <div class="arrow_box" ng-class="{'active' : ctrl.display}"> | |
8 | + <span class="count">{{ctrl.commentsCount > 0 ? ctrl.commentsCount : '+'}}</span> | |
9 | + </div> | |
10 | + </a> | |
11 | + </div> | |
12 | +</div> | ... | ... |
src/plugins/comment_paragraph/allow-comment/allow-comment.scss
0 → 100644
... | ... | @@ -0,0 +1,62 @@ |
1 | +$balloon-selected-color: #50BF68; | |
2 | +$balloon-color: #c4c4c4; | |
3 | + | |
4 | +comment-paragraph-plugin-allow-comment { | |
5 | + .paragraph { | |
6 | + width: 100%; | |
7 | + &.active { | |
8 | + width: 80%; | |
9 | + } | |
10 | + .popover { | |
11 | + &.right > .arrow { | |
12 | + } | |
13 | + } | |
14 | + .paragraph-content { | |
15 | + width: 95%; | |
16 | + display: inline-block; | |
17 | + } | |
18 | + .actions { | |
19 | + width: 3%; | |
20 | + display: inline-block; | |
21 | + vertical-align: top; | |
22 | + margin-left: 10px; | |
23 | + .popover { | |
24 | + width: 100%; | |
25 | + max-width: 330px; | |
26 | + } | |
27 | + .count { | |
28 | + font-size: 14px; | |
29 | + font-weight: bold; | |
30 | + color: white; | |
31 | + text-align: center; | |
32 | + width: 100%; | |
33 | + display: inline-block; | |
34 | + } | |
35 | + .arrow_box { | |
36 | + position: relative; | |
37 | + background: $balloon-color; | |
38 | + margin-top: 5px; | |
39 | + width: 25px; | |
40 | + border-radius: 2px; | |
41 | + &:after { | |
42 | + top: 100%; | |
43 | + left: 50%; | |
44 | + border: solid transparent; | |
45 | + content: " "; | |
46 | + position: absolute; | |
47 | + pointer-events: none; | |
48 | + border-color: rgba(196, 196, 196, 0); | |
49 | + border-top-color: $balloon-color; | |
50 | + border-width: 6px; | |
51 | + margin-left: -6px; | |
52 | + } | |
53 | + &:hover, &.active { | |
54 | + background: $balloon-selected-color; | |
55 | + &:after { | |
56 | + border-top-color: $balloon-selected-color; | |
57 | + } | |
58 | + } | |
59 | + } | |
60 | + } | |
61 | + } | |
62 | +} | ... | ... |
src/plugins/comment_paragraph/allow-comment/popover.html
0 → 100644
... | ... | @@ -0,0 +1 @@ |
1 | +<comment-paragraph-side-comments id="side-comments-{{ctrl.paragraphUuid}}" click-outside="ctrl.hideParagraphComments()" [article]="ctrl.article" [paragraph-uuid]="ctrl.paragraphUuid"></comment-paragraph-side-comments> | ... | ... |
src/plugins/comment_paragraph/events/comment-paragraph-event.service.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,28 @@ |
1 | +import {CommentParagraphEventService} from "./comment-paragraph-event.service"; | |
2 | +import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; | |
3 | +import * as helpers from "../../../spec/helpers"; | |
4 | +import {Provider} from 'ng-forward'; | |
5 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
6 | + | |
7 | +describe("Services", () => { | |
8 | + describe("Comment Paragraph Event Service", () => { | |
9 | + let eventService: CommentParagraphEventService; | |
10 | + | |
11 | + beforeEach(() => { | |
12 | + eventService = new CommentParagraphEventService(); | |
13 | + eventService['toggleCommentParagraphEmitter'] = jasmine.createSpyObj("toggleCommentParagraphEmitter", ["next", "subscribe"]); | |
14 | + }); | |
15 | + | |
16 | + it('subscribe to toggle comment paragraph event', () => { | |
17 | + eventService['toggleCommentParagraphEmitter'].subscribe = jasmine.createSpy("subscribe"); | |
18 | + eventService.subscribeToggleCommentParagraph(() => { }); | |
19 | + expect(eventService['toggleCommentParagraphEmitter'].subscribe).toHaveBeenCalled(); | |
20 | + }); | |
21 | + | |
22 | + it('emit event when toggle comment paragraph', () => { | |
23 | + eventService['toggleCommentParagraphEmitter'].subscribe = jasmine.createSpy("next"); | |
24 | + eventService.toggleCommentParagraph(<noosfero.Article>{}); | |
25 | + expect(eventService['toggleCommentParagraphEmitter'].next).toHaveBeenCalled(); | |
26 | + }); | |
27 | + }); | |
28 | +}); | ... | ... |
src/plugins/comment_paragraph/events/comment-paragraph-event.service.ts
0 → 100644
... | ... | @@ -0,0 +1,19 @@ |
1 | +import {Injectable, EventEmitter} from "ng-forward"; | |
2 | + | |
3 | +@Injectable() | |
4 | +export class CommentParagraphEventService { | |
5 | + | |
6 | + private toggleCommentParagraphEmitter: EventEmitter<noosfero.Article>; | |
7 | + | |
8 | + constructor() { | |
9 | + this.toggleCommentParagraphEmitter = new EventEmitter(); | |
10 | + } | |
11 | + | |
12 | + toggleCommentParagraph(article: noosfero.Article) { | |
13 | + this.toggleCommentParagraphEmitter.next(article); | |
14 | + } | |
15 | + | |
16 | + subscribeToggleCommentParagraph(fn: (article: noosfero.Article) => void) { | |
17 | + this.toggleCommentParagraphEmitter.subscribe(fn); | |
18 | + } | |
19 | +} | ... | ... |
src/plugins/comment_paragraph/hotspot/comment-paragraph-article-button.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,85 @@ |
1 | +import {CommentParagraphArticleButtonHotspotComponent} from "./comment-paragraph-article-button.component"; | |
2 | +import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; | |
3 | +import * as helpers from "../../../spec/helpers"; | |
4 | +import {Provider} from 'ng-forward'; | |
5 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
6 | + | |
7 | +let htmlTemplate = '<comment-paragraph-article-button-hotspot [article]="ctrl.article"></comment-paragraph-article-button-hotspot>'; | |
8 | + | |
9 | +describe("Components", () => { | |
10 | + describe("Comment Paragraph Article Button Hotspot Component", () => { | |
11 | + | |
12 | + let serviceMock = jasmine.createSpyObj("CommentParagraphService", ["deactivateCommentParagraph", "activateCommentParagraph"]); | |
13 | + let eventServiceMock = jasmine.createSpyObj("CommentParagraphEventService", ["toggleCommentParagraph"]); | |
14 | + | |
15 | + let providers = [ | |
16 | + new Provider('CommentParagraphService', { useValue: serviceMock }), | |
17 | + new Provider('CommentParagraphEventService', { useValue: eventServiceMock }) | |
18 | + ].concat(helpers.provideFilters('translateFilter')); | |
19 | + let helper: ComponentTestHelper; | |
20 | + | |
21 | + beforeEach(angular.mock.module("templates")); | |
22 | + | |
23 | + beforeEach((done) => { | |
24 | + let cls = createClass({ | |
25 | + template: htmlTemplate, | |
26 | + directives: [CommentParagraphArticleButtonHotspotComponent], | |
27 | + providers: providers, | |
28 | + properties: { | |
29 | + article: {} | |
30 | + } | |
31 | + }); | |
32 | + helper = new ComponentTestHelper(cls, done); | |
33 | + }); | |
34 | + | |
35 | + it('emit event when deactivate comment paragraph in an article', () => { | |
36 | + serviceMock.deactivateCommentParagraph = jasmine.createSpy("deactivateCommentParagraph").and.returnValue( | |
37 | + { then: (fn: Function) => { fn({ data: {} }); } } | |
38 | + ); | |
39 | + eventServiceMock.toggleCommentParagraph = jasmine.createSpy("toggleCommentParagraph"); | |
40 | + helper.component.deactivateCommentParagraph(); | |
41 | + | |
42 | + expect(serviceMock.deactivateCommentParagraph).toHaveBeenCalled(); | |
43 | + expect(eventServiceMock.toggleCommentParagraph).toHaveBeenCalled(); | |
44 | + }); | |
45 | + | |
46 | + it('emit event when activate comment paragraph in an article', () => { | |
47 | + serviceMock.activateCommentParagraph = jasmine.createSpy("activateCommentParagraph").and.returnValue( | |
48 | + { then: (fn: Function) => { fn({ data: {} }); } } | |
49 | + ); | |
50 | + eventServiceMock.toggleCommentParagraph = jasmine.createSpy("toggleCommentParagraph"); | |
51 | + helper.component.activateCommentParagraph(); | |
52 | + | |
53 | + expect(serviceMock.activateCommentParagraph).toHaveBeenCalled(); | |
54 | + expect(eventServiceMock.toggleCommentParagraph).toHaveBeenCalled(); | |
55 | + }); | |
56 | + | |
57 | + it('return true when comment paragraph is active', () => { | |
58 | + helper.component.article = { setting: { comment_paragraph_plugin_activate: true } }; | |
59 | + helper.detectChanges(); | |
60 | + expect(helper.component.isActivated()).toBeTruthy(); | |
61 | + }); | |
62 | + | |
63 | + it('return false when comment paragraph is not active', () => { | |
64 | + expect(helper.component.isActivated()).toBeFalsy(); | |
65 | + }); | |
66 | + | |
67 | + it('return false when article has no setting attribute', () => { | |
68 | + helper.component.article = {}; | |
69 | + helper.detectChanges(); | |
70 | + expect(helper.component.isActivated()).toBeFalsy(); | |
71 | + }); | |
72 | + | |
73 | + it('display activate button when comment paragraph is not active', () => { | |
74 | + expect(helper.all('.comment-paragraph-activate').length).toEqual(1); | |
75 | + expect(helper.all('.comment-paragraph-deactivate').length).toEqual(0); | |
76 | + }); | |
77 | + | |
78 | + it('display deactivate button when comment paragraph is active', () => { | |
79 | + helper.component.article = { setting: { comment_paragraph_plugin_activate: true } }; | |
80 | + helper.detectChanges(); | |
81 | + expect(helper.all('.comment-paragraph-deactivate').length).toEqual(1); | |
82 | + expect(helper.all('.comment-paragraph-activate').length).toEqual(0); | |
83 | + }); | |
84 | + }); | |
85 | +}); | ... | ... |
src/plugins/comment_paragraph/hotspot/comment-paragraph-article-button.component.ts
0 → 100644
... | ... | @@ -0,0 +1,38 @@ |
1 | +import { Input, Inject, Component } from "ng-forward"; | |
2 | +import {Hotspot} from "../../../app/hotspot/hotspot.decorator"; | |
3 | +import {CommentParagraphService} from "../http/comment-paragraph.service"; | |
4 | +import {CommentParagraphEventService} from "../events/comment-paragraph-event.service"; | |
5 | + | |
6 | +@Component({ | |
7 | + selector: "comment-paragraph-article-button-hotspot", | |
8 | + templateUrl: "plugins/comment_paragraph/hotspot/comment-paragraph-article-button.html", | |
9 | +}) | |
10 | +@Inject("$scope", CommentParagraphService, CommentParagraphEventService) | |
11 | +@Hotspot("article_extra_toolbar_buttons") | |
12 | +export class CommentParagraphArticleButtonHotspotComponent { | |
13 | + | |
14 | + @Input() article: noosfero.Article; | |
15 | + | |
16 | + constructor(private $scope: ng.IScope, | |
17 | + private commentParagraphService: CommentParagraphService, | |
18 | + private commentParagraphEventService: CommentParagraphEventService) { } | |
19 | + | |
20 | + deactivateCommentParagraph() { | |
21 | + this.toggleCommentParagraph(this.commentParagraphService.deactivateCommentParagraph(this.article)); | |
22 | + } | |
23 | + | |
24 | + activateCommentParagraph() { | |
25 | + this.toggleCommentParagraph(this.commentParagraphService.activateCommentParagraph(this.article)); | |
26 | + } | |
27 | + | |
28 | + isActivated() { | |
29 | + return this.article && this.article.setting && this.article.setting.comment_paragraph_plugin_activate; | |
30 | + } | |
31 | + | |
32 | + private toggleCommentParagraph(promise: ng.IPromise<noosfero.RestResult<noosfero.Article>>) { | |
33 | + promise.then((result: noosfero.RestResult<noosfero.Article>) => { | |
34 | + this.article = result.data; | |
35 | + this.commentParagraphEventService.toggleCommentParagraph(this.article); | |
36 | + }); | |
37 | + } | |
38 | +} | ... | ... |
src/plugins/comment_paragraph/hotspot/comment-paragraph-article-button.html
0 → 100644
... | ... | @@ -0,0 +1,8 @@ |
1 | +<a href='#' class="btn btn-default btn-xs comment-paragraph-activate" (click)="ctrl.activateCommentParagraph()" | |
2 | + ng-if="!ctrl.article.setting.comment_paragraph_plugin_activate"> | |
3 | + <i class="fa fa-fw fa-plus"></i> {{"comment-paragraph-plugin.title" | translate}} | |
4 | +</a> | |
5 | +<a href='#' class="btn btn-default btn-xs comment-paragraph-deactivate" (click)="ctrl.deactivateCommentParagraph()" | |
6 | + ng-if="ctrl.article.setting.comment_paragraph_plugin_activate"> | |
7 | + <i class="fa fa-fw fa-minus"></i> {{"comment-paragraph-plugin.title" | translate}} | |
8 | +</a> | ... | ... |
src/plugins/comment_paragraph/hotspot/comment-paragraph-form.component.ts
0 → 100644
... | ... | @@ -0,0 +1,26 @@ |
1 | +import { Inject, Input, Component } from "ng-forward"; | |
2 | +import {Hotspot} from "../../../app/hotspot/hotspot.decorator"; | |
3 | + | |
4 | +@Component({ | |
5 | + selector: "comment-paragraph-form-hotspot", | |
6 | + template: "<span></span>", | |
7 | +}) | |
8 | +@Hotspot("comment_form_extra_contents") | |
9 | +@Inject("$scope") | |
10 | +export class CommentParagraphFormHotspotComponent { | |
11 | + | |
12 | + @Input() comment: noosfero.Comment; | |
13 | + @Input() parent: noosfero.Comment; | |
14 | + | |
15 | + constructor(private $scope: ng.IScope) { } | |
16 | + | |
17 | + ngOnInit() { | |
18 | + this.$scope.$watch(() => { | |
19 | + return this.parent; | |
20 | + }, () => { | |
21 | + if (this.parent && (<any>this.parent).paragraph_uuid) { | |
22 | + (<any>this.comment).paragraph_uuid = (<any>this.parent).paragraph_uuid; | |
23 | + } | |
24 | + }); | |
25 | + } | |
26 | +} | ... | ... |
src/plugins/comment_paragraph/hotspot/comment-paragraph-from.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,33 @@ |
1 | +import {CommentParagraphFormHotspotComponent} from "./comment-paragraph-form.component"; | |
2 | +import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; | |
3 | +import * as helpers from "../../../spec/helpers"; | |
4 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
5 | + | |
6 | +let htmlTemplate = '<comment-paragraph-form-hotspot [comment]="ctrl.comment" [parent]="ctrl.parent"></comment-paragraph-form-hotspot>'; | |
7 | + | |
8 | +describe("Components", () => { | |
9 | + describe("Comment Paragraph Form Hotspot Component", () => { | |
10 | + | |
11 | + let helper: ComponentTestHelper; | |
12 | + | |
13 | + beforeEach(angular.mock.module("templates")); | |
14 | + | |
15 | + beforeEach((done) => { | |
16 | + let cls = createClass({ | |
17 | + template: htmlTemplate, | |
18 | + directives: [CommentParagraphFormHotspotComponent], | |
19 | + providers: [], | |
20 | + properties: { | |
21 | + comment: {} | |
22 | + } | |
23 | + }); | |
24 | + helper = new ComponentTestHelper(cls, done); | |
25 | + }); | |
26 | + | |
27 | + it('set paragraph uuid when parent has it setted', () => { | |
28 | + helper.component.parent = { paragraph_uuid: 'uuid' }; | |
29 | + helper.detectChanges(); | |
30 | + expect((<any>helper.component.comment).paragraph_uuid).toEqual('uuid'); | |
31 | + }); | |
32 | + }); | |
33 | +}); | ... | ... |
src/plugins/comment_paragraph/http/comment-paragraph.service.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,90 @@ |
1 | +import {CommentParagraphService} from "./comment-paragraph.service"; | |
2 | + | |
3 | + | |
4 | +describe("Services", () => { | |
5 | + | |
6 | + describe("Comment Paragraph Service", () => { | |
7 | + | |
8 | + let $httpBackend: ng.IHttpBackendService; | |
9 | + let commentParagraphService: CommentParagraphService; | |
10 | + | |
11 | + beforeEach(angular.mock.module("noosferoApp", ($translateProvider: angular.translate.ITranslateProvider) => { | |
12 | + $translateProvider.translations('en', {}); | |
13 | + })); | |
14 | + | |
15 | + beforeEach(inject((_$httpBackend_: ng.IHttpBackendService, _CommentParagraphService_: CommentParagraphService) => { | |
16 | + $httpBackend = _$httpBackend_; | |
17 | + commentParagraphService = _CommentParagraphService_; | |
18 | + })); | |
19 | + | |
20 | + | |
21 | + describe("Succesfull requests", () => { | |
22 | + | |
23 | + it("should return paragraph comments by article", (done) => { | |
24 | + let articleId = 1; | |
25 | + $httpBackend.expectGET(`/api/v1/articles/${articleId}/comment_paragraph_plugin/comments?without_reply=true`).respond(200, { comments: [{ body: "comment1" }] }); | |
26 | + commentParagraphService.getByArticle(<noosfero.Article>{ id: articleId }).then((result: noosfero.RestResult<noosfero.Comment[]>) => { | |
27 | + expect(result.data).toEqual([{ body: "comment1" }]); | |
28 | + done(); | |
29 | + }); | |
30 | + $httpBackend.flush(); | |
31 | + }); | |
32 | + | |
33 | + it("should create a paragraph comment", (done) => { | |
34 | + let articleId = 1; | |
35 | + $httpBackend.expectPOST(`/api/v1/articles/${articleId}/comment_paragraph_plugin/comments`).respond(200, { comment: { body: "comment1" } }); | |
36 | + commentParagraphService.createInArticle(<noosfero.Article>{ id: articleId }, <noosfero.Comment>{ body: "comment1" }).then((result: noosfero.RestResult<noosfero.Comment>) => { | |
37 | + expect(result.data).toEqual({ body: "comment1" }); | |
38 | + done(); | |
39 | + }); | |
40 | + $httpBackend.flush(); | |
41 | + }); | |
42 | + | |
43 | + it("activate paragraph comments for an article", (done) => { | |
44 | + let articleId = 1; | |
45 | + $httpBackend.expectPOST(`/api/v1/articles/${articleId}/comment_paragraph_plugin/activate`).respond(200, { article: { title: "article1" } }); | |
46 | + commentParagraphService.activateCommentParagraph(<noosfero.Article>{ id: articleId }).then((result: noosfero.RestResult<noosfero.Article>) => { | |
47 | + expect(result.data).toEqual({ title: "article1" }); | |
48 | + done(); | |
49 | + }); | |
50 | + $httpBackend.flush(); | |
51 | + }); | |
52 | + | |
53 | + it("deactivate paragraph comments for an article", (done) => { | |
54 | + let articleId = 1; | |
55 | + $httpBackend.expectPOST(`/api/v1/articles/${articleId}/comment_paragraph_plugin/deactivate`).respond(200, { article: { title: "article1" } }); | |
56 | + commentParagraphService.deactivateCommentParagraph(<noosfero.Article>{ id: articleId }).then((result: noosfero.RestResult<noosfero.Article>) => { | |
57 | + expect(result.data).toEqual({ title: "article1" }); | |
58 | + done(); | |
59 | + }); | |
60 | + $httpBackend.flush(); | |
61 | + }); | |
62 | + | |
63 | + it("return counts for paragraph comments", (done) => { | |
64 | + let articleId = 1; | |
65 | + $httpBackend.expectGET(`/api/v1/articles/${articleId}/comment_paragraph_plugin/comments/count`).respond(200, { '1': 5, '2': 6 }); | |
66 | + commentParagraphService.commentParagraphCount(<noosfero.Article>{ id: articleId }, '1').then((count: number) => { | |
67 | + expect(count).toEqual(5); | |
68 | + done(); | |
69 | + }); | |
70 | + $httpBackend.flush(); | |
71 | + }); | |
72 | + | |
73 | + it("reset promise when comment paragraph counts fails", (done) => { | |
74 | + let articleId = 1; | |
75 | + commentParagraphService['articleService'] = jasmine.createSpyObj("articleService", ["getElement"]); | |
76 | + commentParagraphService['articleService'].getElement = jasmine.createSpy("getElement").and.returnValue( | |
77 | + { | |
78 | + customGET: (path: string) => { | |
79 | + return Promise.reject({}); | |
80 | + } | |
81 | + } | |
82 | + ); | |
83 | + commentParagraphService.commentParagraphCount(<noosfero.Article>{ id: articleId }, '1').catch(() => { | |
84 | + expect(commentParagraphService['commentParagraphCountsPromise']).toBeNull(); | |
85 | + done(); | |
86 | + }); | |
87 | + }); | |
88 | + }); | |
89 | + }); | |
90 | +}); | ... | ... |
src/plugins/comment_paragraph/http/comment-paragraph.service.ts
0 → 100644
... | ... | @@ -0,0 +1,64 @@ |
1 | +import { Injectable, Inject } from "ng-forward"; | |
2 | +import {RestangularService} from "../../../lib/ng-noosfero-api/http/restangular_service"; | |
3 | +import {ArticleService} from "../../../lib/ng-noosfero-api/http/article.service"; | |
4 | + | |
5 | +@Injectable() | |
6 | +@Inject("Restangular", "$q", "$log", ArticleService) | |
7 | +export class CommentParagraphService extends RestangularService<noosfero.Comment> { | |
8 | + | |
9 | + private commentParagraphCountsPromise: ng.IPromise<any>; | |
10 | + | |
11 | + constructor(Restangular: restangular.IService, $q: ng.IQService, $log: ng.ILogService, protected articleService: ArticleService) { | |
12 | + super(Restangular, $q, $log); | |
13 | + } | |
14 | + | |
15 | + getResourcePath() { | |
16 | + return "comment_paragraph_plugin/comments"; | |
17 | + } | |
18 | + | |
19 | + getDataKeys() { | |
20 | + return { | |
21 | + singular: 'comment', | |
22 | + plural: 'comments' | |
23 | + }; | |
24 | + } | |
25 | + | |
26 | + getByArticle(article: noosfero.Article, params: any = {}): ng.IPromise<noosfero.RestResult<noosfero.Comment[]>> { | |
27 | + params['without_reply'] = true; | |
28 | + let articleElement = this.articleService.getElement(<number>article.id); | |
29 | + return this.list(articleElement, params); | |
30 | + } | |
31 | + | |
32 | + createInArticle(article: noosfero.Article, comment: noosfero.Comment): ng.IPromise<noosfero.RestResult<noosfero.Comment>> { | |
33 | + let articleElement = this.articleService.getElement(<number>article.id); | |
34 | + return this.create(comment, articleElement, null, { 'Content-Type': 'application/json' }, false); | |
35 | + } | |
36 | + | |
37 | + activateCommentParagraph(article: noosfero.Article) { | |
38 | + let articleElement = this.articleService.getElement(<number>article.id); | |
39 | + return this.articleService.post("comment_paragraph_plugin/activate", articleElement); | |
40 | + } | |
41 | + | |
42 | + deactivateCommentParagraph(article: noosfero.Article) { | |
43 | + let articleElement = this.articleService.getElement(<number>article.id); | |
44 | + return this.articleService.post("comment_paragraph_plugin/deactivate", articleElement); | |
45 | + } | |
46 | + | |
47 | + commentParagraphCount(article: noosfero.Article, paragraphUuid: string) { | |
48 | + return this.commentParagraphCounts(article).then((counts: any) => { | |
49 | + return counts[paragraphUuid]; | |
50 | + }); | |
51 | + } | |
52 | + | |
53 | + private commentParagraphCounts(article: noosfero.Article) { | |
54 | + if (!this.commentParagraphCountsPromise) { | |
55 | + let articleElement = this.articleService.getElement(<number>article.id); | |
56 | + this.commentParagraphCountsPromise = articleElement.customGET("comment_paragraph_plugin/comments/count").then((response: restangular.IResponse) => { | |
57 | + return response.data; | |
58 | + }).catch(() => { | |
59 | + this.commentParagraphCountsPromise = null; | |
60 | + }); | |
61 | + } | |
62 | + return this.commentParagraphCountsPromise; | |
63 | + } | |
64 | +} | ... | ... |
... | ... | @@ -0,0 +1,6 @@ |
1 | +import {AllowCommentComponent} from "./allow-comment/allow-comment.component"; | |
2 | +import {CommentParagraphArticleButtonHotspotComponent} from "./hotspot/comment-paragraph-article-button.component"; | |
3 | +import {CommentParagraphFormHotspotComponent} from "./hotspot/comment-paragraph-form.component"; | |
4 | + | |
5 | +export let mainComponents: any = [AllowCommentComponent]; | |
6 | +export let hotspots: any = [CommentParagraphArticleButtonHotspotComponent, CommentParagraphFormHotspotComponent]; | ... | ... |
src/plugins/comment_paragraph/side-comments/side-comments.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,50 @@ |
1 | +import {SideCommentsComponent} from "./side-comments.component"; | |
2 | +import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; | |
3 | +import * as helpers from "../../../spec/helpers"; | |
4 | +import {Provider} from 'ng-forward'; | |
5 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
6 | + | |
7 | +let htmlTemplate = '<comment-paragraph-side-comments [article]="ctrl.article" [paragraph-uuid]="ctrl.paragraphUuid"></comment-paragraph-side-comments>'; | |
8 | + | |
9 | +describe("Components", () => { | |
10 | + describe("Side Comments Component", () => { | |
11 | + | |
12 | + let serviceMock = jasmine.createSpyObj("CommentParagraphService", ["getByArticle"]); | |
13 | + serviceMock.getByArticle = jasmine.createSpy("getByArticle").and.returnValue(Promise.resolve({ data: {} })); | |
14 | + | |
15 | + let commentServiceMock = {}; | |
16 | + let postCommentEventService = jasmine.createSpyObj("postCommentEventService", ["emit", "subscribe"]); | |
17 | + postCommentEventService.subscribe = jasmine.createSpy("subscribe"); | |
18 | + | |
19 | + let providers = [ | |
20 | + new Provider('CommentParagraphService', { useValue: serviceMock }), | |
21 | + new Provider('CommentService', { useValue: commentServiceMock }), | |
22 | + new Provider('PostCommentEventService', { useValue: postCommentEventService }) | |
23 | + ]; | |
24 | + let helper: ComponentTestHelper; | |
25 | + | |
26 | + beforeEach(angular.mock.module("templates")); | |
27 | + | |
28 | + beforeEach((done) => { | |
29 | + let cls = createClass({ | |
30 | + template: htmlTemplate, | |
31 | + directives: [SideCommentsComponent], | |
32 | + providers: providers, | |
33 | + properties: { | |
34 | + paragraphUuid: "uuid", | |
35 | + article: {} | |
36 | + } | |
37 | + }); | |
38 | + helper = new ComponentTestHelper(cls, done); | |
39 | + }); | |
40 | + | |
41 | + it('call service to load paragraph comments', () => { | |
42 | + helper.component.loadComments(); | |
43 | + expect(serviceMock.getByArticle).toHaveBeenCalled(); | |
44 | + }); | |
45 | + | |
46 | + it('set paragraph uuid in new comment object', () => { | |
47 | + expect(helper.component.newComment['paragraph_uuid']).toEqual('uuid'); | |
48 | + }); | |
49 | + }); | |
50 | +}); | ... | ... |
src/plugins/comment_paragraph/side-comments/side-comments.component.ts
0 → 100644
... | ... | @@ -0,0 +1,32 @@ |
1 | +import {Component, Inject, Input, Output} from "ng-forward"; | |
2 | +import {CommentsComponent} from "../../../app/article/comment/comments.component"; | |
3 | +import {CommentService} from "../../../lib/ng-noosfero-api/http/comment.service"; | |
4 | +import {CommentParagraphService} from "../http/comment-paragraph.service"; | |
5 | +import { PostCommentEventService } from "../../../app/article/comment/post-comment/post-comment-event.service"; | |
6 | + | |
7 | +@Component({ | |
8 | + selector: "comment-paragraph-side-comments", | |
9 | + templateUrl: 'app/article/comment/comments.html', | |
10 | +}) | |
11 | +@Inject(CommentService, PostCommentEventService, "$scope", CommentParagraphService) | |
12 | +export class SideCommentsComponent extends CommentsComponent { | |
13 | + | |
14 | + @Input() article: noosfero.Article; | |
15 | + @Input() paragraphUuid: string; | |
16 | + | |
17 | + constructor(commentService: CommentService, | |
18 | + postCommentEventService: PostCommentEventService, | |
19 | + $scope: ng.IScope, | |
20 | + private commentParagraphService: CommentParagraphService) { | |
21 | + super(commentService, postCommentEventService, $scope); | |
22 | + } | |
23 | + | |
24 | + ngOnInit() { | |
25 | + super.ngOnInit(); | |
26 | + (<any>this.newComment).paragraph_uuid = this.paragraphUuid; | |
27 | + } | |
28 | + | |
29 | + loadComments() { | |
30 | + return this.commentParagraphService.getByArticle(this.article, { page: this.page, per_page: this.perPage, paragraph_uuid: this.paragraphUuid }); | |
31 | + } | |
32 | +} | ... | ... |
src/plugins/comment_paragraph/side-comments/side-comments.scss
0 → 100644
... | ... | @@ -0,0 +1,7 @@ |
1 | +import * as commentParagraph from "./comment_paragraph"; | |
2 | + | |
3 | +export let mainComponents: any = []; | |
4 | +mainComponents = mainComponents.concat(commentParagraph.mainComponents); | |
5 | + | |
6 | +export let hotspots: any = []; | |
7 | +hotspots = hotspots.concat(commentParagraph.hotspots); | ... | ... |
src/spec/component-test-helper.ts
... | ... | @@ -68,6 +68,8 @@ export class ComponentTestHelper<T extends any> { |
68 | 68 | this.init(done); |
69 | 69 | } |
70 | 70 | |
71 | + fixture: any; | |
72 | + | |
71 | 73 | /** |
72 | 74 | * @ngdoc method |
73 | 75 | * @name init |
... | ... | @@ -79,7 +81,8 @@ export class ComponentTestHelper<T extends any> { |
79 | 81 | let promisse = this.tcb.createAsync(this.mockComponent) as Promise<ComponentFixture>; |
80 | 82 | return promisse.then((fixture: any) => { |
81 | 83 | // Fire all angular events and parsing |
82 | - fixture.detectChanges(); | |
84 | + this.fixture = fixture; | |
85 | + this.detectChanges(); | |
83 | 86 | // The main debug element |
84 | 87 | this.debugElement = fixture.debugElement; |
85 | 88 | this.component = <T>this.debugElement.componentViewChildren[0].componentInstance; |
... | ... | @@ -96,6 +99,17 @@ export class ComponentTestHelper<T extends any> { |
96 | 99 | |
97 | 100 | /** |
98 | 101 | * @ngdoc method |
102 | + * @name detectChanges | |
103 | + * @methodOf spec.ComponentTestHelper | |
104 | + * @description | |
105 | + * Detect changes in component | |
106 | + */ | |
107 | + detectChanges() { | |
108 | + this.fixture.detectChanges(); | |
109 | + } | |
110 | + | |
111 | + /** | |
112 | + * @ngdoc method | |
99 | 113 | * @name all |
100 | 114 | * @methodOf spec.ComponentTestHelper |
101 | 115 | * @description | ... | ... |