Merge Request #37
-
…ith the query value in the current query param. changed the search.component to have a search text box to show then current query and allow the user query for new terms.
| ... | ... | @@ -2,6 +2,7 @@ import {StateConfig, Component, Inject, provide} from 'ng-forward'; |
| 2 | 2 | import {EnvironmentService} from "../../lib/ng-noosfero-api/http/environment.service"; |
| 3 | 3 | import {NotificationService} from "../shared/services/notification.service"; |
| 4 | 4 | import {EnvironmentHomeComponent} from "./environment-home.component"; |
| 5 | +import {SearchComponent} from "../search/search.component"; | |
| 5 | 6 | |
| 6 | 7 | /** |
| 7 | 8 | * @ngdoc controller |
| ... | ... | @@ -29,6 +30,18 @@ import {EnvironmentHomeComponent} from "./environment-home.component"; |
| 29 | 30 | controllerAs: "vm" |
| 30 | 31 | } |
| 31 | 32 | } |
| 33 | + }, | |
| 34 | + { | |
| 35 | + url: '^/search?query', | |
| 36 | + component: SearchComponent, | |
| 37 | + name: 'main.environment.search', | |
| 38 | + views: { | |
| 39 | + "mainBlockContent": { | |
| 40 | + templateUrl: "app/search/search.html", | |
| 41 | + controller: SearchComponent, | |
| 42 | + controllerAs: "ctrl" | |
| 43 | + } | |
| 44 | + } | |
| 32 | 45 | } |
| 33 | 46 | ]) |
| 34 | 47 | @Inject(EnvironmentService, "$state", "currentEnvironment") | ... | ... |
| ... | ... | @@ -43,6 +43,9 @@ |
| 43 | 43 | <language-selector class="nav navbar-nav navbar-right"></language-selector> |
| 44 | 44 | </ul> |
| 45 | 45 | <div ui-view="actions"></div> |
| 46 | + <div class="nav navbar-nav search navbar-right"> | |
| 47 | + <search-form></search-form> | |
| 48 | + </div> | |
| 46 | 49 | </div> |
| 47 | 50 | </div> |
| 48 | 51 | </nav> | ... | ... |
| ... | ... | @@ -34,6 +34,16 @@ |
| 34 | 34 | text-align: center; |
| 35 | 35 | } |
| 36 | 36 | |
| 37 | + .search { | |
| 38 | + .search-input { | |
| 39 | + height: 45px; | |
| 40 | + margin-top: 10px; | |
| 41 | + } | |
| 42 | + .input-group-btn { | |
| 43 | + padding-top: 10px; | |
| 44 | + } | |
| 45 | + } | |
| 46 | + | |
| 37 | 47 | @media (max-width: $break-sm-max) { |
| 38 | 48 | .navbar-toggle .fa-bars{ |
| 39 | 49 | font-size: 14pt; | ... | ... |
| ... | ... | @@ -180,27 +180,19 @@ $whbl-font-color: #16191c; |
| 180 | 180 | } |
| 181 | 181 | .pagination { |
| 182 | 182 | > li { |
| 183 | - > a, | |
| 184 | - > span, | |
| 185 | - > a:hover, | |
| 186 | - > span:hover, | |
| 187 | - > a:focus, | |
| 188 | - > span:focus, | |
| 189 | - > a:active, | |
| 190 | - > span:active { | |
| 191 | - color: $whbl-primary-color; | |
| 183 | + &.active { | |
| 184 | + a { | |
| 185 | + background-color: $whbl-primary-color; | |
| 186 | + color: #FFF; | |
| 187 | + } | |
| 192 | 188 | } |
| 193 | - } | |
| 194 | - > .active { | |
| 195 | - > a, | |
| 196 | - > span, | |
| 197 | - > a:hover, | |
| 198 | - > span:hover, | |
| 199 | - > a:focus, | |
| 200 | - > span:focus { | |
| 201 | - background-color: $whbl-primary-color; | |
| 202 | - border-color: $whbl-primary-color; | |
| 203 | - color: #fff; | |
| 189 | + > a { | |
| 190 | + &:hover { | |
| 191 | + background-color: $whbl-primary-color; | |
| 192 | + color: #FFF; | |
| 193 | + } | |
| 194 | + background-color: #FFF; | |
| 195 | + color: $whbl-primary-color; | |
| 204 | 196 | } |
| 205 | 197 | } |
| 206 | 198 | } | ... | ... |
| ... | ... | @@ -40,6 +40,8 @@ import {SidebarComponent} from "../layout/sidebar/sidebar.component"; |
| 40 | 40 | import {MainBlockComponent} from "../layout/blocks/main/main-block.component"; |
| 41 | 41 | import {HtmlEditorComponent} from "../shared/components/html-editor/html-editor.component"; |
| 42 | 42 | import {PermissionDirective} from "../shared/components/permission/permission.directive"; |
| 43 | +import {SearchComponent} from "../search/search.component"; | |
| 44 | +import {SearchFormComponent} from "../search/search-form/search-form.component"; | |
| 43 | 45 | |
| 44 | 46 | /** |
| 45 | 47 | * @ngdoc controller |
| ... | ... | @@ -100,7 +102,7 @@ export class EnvironmentContent { |
| 100 | 102 | LinkListBlockComponent, CommunitiesBlockComponent, HtmlEditorComponent, ProfileComponent, |
| 101 | 103 | MainBlockComponent, RecentDocumentsBlockComponent, Navbar, SidebarComponent, ProfileImageBlockComponent, |
| 102 | 104 | MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent, StatisticsBlockComponent, |
| 103 | - LoginBlockComponent, CustomContentComponent, PermissionDirective | |
| 105 | + LoginBlockComponent, CustomContentComponent, PermissionDirective, SearchFormComponent, SearchComponent | |
| 104 | 106 | ].concat(plugins.mainComponents).concat(plugins.hotspots), |
| 105 | 107 | providers: [AuthService, SessionService, NotificationService, BodyStateClassesService, |
| 106 | 108 | "ngAnimate", "ngCookies", "ngStorage", "ngTouch", | ... | ... |
| ... | ... | @@ -0,0 +1,34 @@ |
| 1 | +import {ComponentTestHelper, createClass} from "../../../spec/component-test-helper"; | |
| 2 | +import {SearchFormComponent} from "./search-form.component"; | |
| 3 | +import * as helpers from "../../../spec/helpers"; | |
| 4 | + | |
| 5 | +const htmlTemplate: string = '<search-form></search-form>'; | |
| 6 | + | |
| 7 | +describe("Components", () => { | |
| 8 | + describe("Search Form Component", () => { | |
| 9 | + | |
| 10 | + let helper: ComponentTestHelper<SearchFormComponent>; | |
| 11 | + let stateMock = jasmine.createSpyObj("$state", ["go"]); | |
| 12 | + | |
| 13 | + beforeEach(angular.mock.module("templates")); | |
| 14 | + | |
| 15 | + beforeEach((done) => { | |
| 16 | + let cls = createClass({ | |
| 17 | + template: htmlTemplate, | |
| 18 | + directives: [SearchFormComponent], | |
| 19 | + providers: [helpers.createProviderToValue("$state", stateMock)] | |
| 20 | + }); | |
| 21 | + helper = new ComponentTestHelper<SearchFormComponent>(cls, done); | |
| 22 | + }); | |
| 23 | + | |
| 24 | + it("render a input for search query", () => { | |
| 25 | + expect(helper.find(".search-input").length).toEqual(1); | |
| 26 | + }); | |
| 27 | + | |
| 28 | + it("go to search page when click on search button", () => { | |
| 29 | + helper.component.query = 'query'; | |
| 30 | + helper.component.search(); | |
| 31 | + expect(stateMock.go).toHaveBeenCalledWith('main.environment.search', { query: 'query' }); | |
| 32 | + }); | |
| 33 | + }); | |
| 34 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,26 @@ |
| 1 | +import {Component, Inject} from "ng-forward"; | |
| 2 | + | |
| 3 | +@Component({ | |
| 4 | + selector: 'search-form', | |
| 5 | + templateUrl: 'app/search/search-form/search-form.html' | |
| 6 | +}) | |
| 7 | +@Inject("$state") | |
| 8 | +export class SearchFormComponent { | |
| 2 |
|
|
| 9 | + | |
| 10 | + query: string; | |
| 11 | + | |
| 12 | + constructor(private $state: ng.ui.IStateService) { | |
| 13 | + } | |
| 14 | + | |
| 15 | + ngOnInit() { | |
| 16 | + this.query = this.$state.params['query']; | |
| 17 | + } | |
| 18 | + | |
| 19 | + search() { | |
| 20 | + this.$state.go('main.environment.search', { query: this.query }); | |
| 21 | + } | |
| 22 | + | |
| 23 | + isSearchPage() { | |
| 24 | + return "main.environment.search" === this.$state.current.name; | |
| 25 | + } | |
| 26 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | +<form class="navbar-form search-form" role="search" ng-if="!ctrl.isSearchPage()"> | |
| 2 | + <div class="input-group"> | |
| 3 | + <input type="text" class="search-input form-control" placeholder="Search" name="q" ng-model="ctrl.query"> | |
| 4 | + <div class="input-group-btn"> | |
| 5 | + <button class="btn btn-default" type="submit" (click)="ctrl.search()"><i class="fa fa-search fa-fw"></i></button> | |
| 6 | + </div> | |
| 7 | + </div> | |
| 8 | +</form> | ... | ... |
| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | +import {ComponentTestHelper, createClass} from "../../spec/component-test-helper"; | |
| 2 | +import {SearchComponent} from "./search.component"; | |
| 3 | +import * as helpers from "../../spec/helpers"; | |
| 4 | + | |
| 5 | +const htmlTemplate: string = '<search></search>'; | |
| 6 | + | |
| 7 | +describe("Components", () => { | |
| 8 | + describe("Search Component", () => { | |
| 9 | + | |
| 10 | + let helper: ComponentTestHelper<SearchComponent>; | |
| 11 | + let stateParams = { query: 'query' }; | |
| 12 | + let articleService = jasmine.createSpyObj("ArticleService", ["search"]); | |
| 13 | + let result = Promise.resolve({ data: [{ id: 1 }], headers: (param: string) => { return 1; } }); | |
| 14 | + articleService.search = jasmine.createSpy("search").and.returnValue(result); | |
| 15 | + | |
| 16 | + beforeEach(angular.mock.module("templates")); | |
| 17 | + | |
| 18 | + beforeEach((done) => { | |
| 19 | + let cls = createClass({ | |
| 20 | + template: htmlTemplate, | |
| 21 | + directives: [SearchComponent], | |
| 22 | + providers: [ | |
| 23 | + helpers.createProviderToValue("$stateParams", stateParams), | |
| 24 | + helpers.createProviderToValue("ArticleService", articleService) | |
| 25 | + ].concat(helpers.provideFilters("truncateFilter", "stripTagsFilter")) | |
| 26 | + }); | |
| 27 | + helper = new ComponentTestHelper<SearchComponent>(cls, done); | |
| 28 | + }); | |
| 29 | + | |
| 30 | + it("load first page with search results", () => { | |
| 31 | + expect(articleService.search).toHaveBeenCalledWith({ query: 'query', per_page: 10, page: 0 }); | |
| 32 | + }); | |
| 33 | + | |
| 34 | + it("display search results", () => { | |
| 35 | + expect(helper.all(".result").length).toEqual(1); | |
| 36 | + }); | |
| 37 | + }); | |
| 38 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,40 @@ |
| 1 | +import {Component, Inject} from "ng-forward"; | |
| 2 | +import {ArticleService} from "./../../lib/ng-noosfero-api/http/article.service"; | |
| 3 | + | |
| 4 | +import {SearchFormComponent} from "./search-form/search-form.component"; | |
| 5 | + | |
| 6 | +@Component({ | |
| 7 | + selector: 'search', | |
| 8 | + templateUrl: 'app/search/search.html', | |
| 9 | + directives: [SearchFormComponent] | |
| 10 | +}) | |
| 11 | +@Inject(ArticleService, "$stateParams", "$state") | |
| 12 | +export class SearchComponent { | |
| 13 | + | |
| 14 | + articles: noosfero.Article[]; | |
| 15 | + query: string; | |
| 16 | + totalResults = 0; | |
| 17 | + perPage = 10; | |
| 18 | + currentPage: number = 0; | |
| 19 | + | |
| 20 | + constructor(private articleService: ArticleService, private $stateParams: ng.ui.IStateParamsService, private $state: ng.ui.IStateService) { | |
| 21 | + this.query = this.$stateParams['query']; | |
| 22 | + this.loadPage(); | |
| 23 | + } | |
| 24 | + | |
| 25 | + search() { | |
| 26 | + this.$state.go('main.environment.search', { query: this.query }); | |
| 27 | + } | |
| 28 | + | |
| 29 | + loadPage() { | |
| 30 | + let filters = { | |
| 31 | + query: this.query, | |
| 32 | + per_page: this.perPage, | |
| 33 | + page: this.currentPage | |
| 34 | + }; | |
| 35 | + this.articleService.search(filters).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 36 | + this.totalResults = <number>result.headers("total"); | |
| 37 | + this.articles = result.data; | |
| 38 | + }); | |
| 39 | + } | |
| 40 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | +<form ng-submit="ctrl.search()"> | |
| 2 | +<label for="query" ng-bind-html="'search.results.query.label' | translate"></label> | |
| 3 | +<input id="query" placeholder="{{'search.results.query.placeholder' | translate}}" type="search" class="search-box-title" ng-model="ctrl.query"> | |
| 4 | +</form> | |
| 5 | +<div class="search-results"> | |
| 6 | + <div class="summary"> | |
| 7 | + {{"search.results.summary" | translate:{results: ctrl.totalResults}:"messageformat"}} | |
| 8 | + </div> | |
| 9 | + <div ng-repeat="article in ctrl.articles | orderBy: 'created_at':true" class="result"> | |
| 10 | + <a class="title" ui-sref="main.profile.page({profile: article.profile.identifier, page: article.path})"> | |
| 11 | + <h4 ng-bind="article.title"></h4> | |
| 12 | + </a> | |
| 13 | + <div class="info"> | |
| 14 | + <a class="profile" ui-sref="main.profile.home({profile: article.profile.identifier})"> | |
| 15 | + {{article.profile.name}} | |
| 16 | + </a> | |
| 17 | + <span class="bullet-separator">•</span> | |
| 18 | + <span class="time"> | |
| 19 | + <span am-time-ago="article.created_at | dateFormat"></span> | |
| 20 | + </span> | |
| 21 | + </div> | |
| 22 | + <div class="post-lead" ng-bind-html="article.body | stripTags | truncate: 250: '...': true"></div> | |
| 23 | + </div> | |
| 24 | + <uib-pagination ng-model="ctrl.currentPage" total-items="ctrl.totalResults" class="pagination-sm center-block" | |
| 25 | + boundary-links="true" items-per-page="ctrl.perPage" ng-change="ctrl.loadPage()" | |
| 26 | + first-text="«" last-text="»" previous-text="‹" next-text="›"> | |
| 27 | + </uib-pagination> | |
| 28 | +</div> | ... | ... |
| ... | ... | @@ -0,0 +1,48 @@ |
| 1 | +.search-results { | |
| 2 | + .summary { | |
| 3 | + color: #bbbbbb; | |
| 4 | + font-size: 13px; | |
| 5 | + border-top: 1px solid #ececec; | |
| 6 | + } | |
| 7 | + .result { | |
| 8 | + margin: 25px 0; | |
| 9 | + | |
| 10 | + .title { | |
| 11 | + h4 { | |
| 12 | + margin: 0; | |
| 13 | + } | |
| 14 | + } | |
| 15 | + .info { | |
| 16 | + .profile { | |
| 17 | + color: #6e9e7b; | |
| 1 |
|
|
| 18 | + } | |
| 19 | + .time { | |
| 20 | + color: #6e9e7b; | |
| 21 | + font-size: 12px; | |
| 22 | + } | |
| 23 | + .bullet-separator { | |
| 24 | + margin: 0 2px; | |
| 25 | + font-size: 10px; | |
| 26 | + color: #afd6ba; | |
| 27 | + } | |
| 28 | + } | |
| 29 | + } | |
| 30 | +} | |
| 31 | + | |
| 32 | +.search-box-title { | |
| 33 | + border: 0; | |
| 34 | + border-bottom: 1px solid rgba(0,0,0,.15); | |
| 35 | + border-radius: 0; | |
| 36 | + font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Open Sans","Helvetica Neue",sans-serif; | |
| 37 | + letter-spacing: 0; | |
| 38 | + font-weight: 300; | |
| 39 | + font-style: normal; | |
| 40 | + font-size: 50px; | |
| 41 | + height: 80px; | |
| 42 | + padding: 0; | |
| 43 | + width: 100%; | |
| 44 | +} | |
| 45 | + | |
| 46 | +.search-box-title:focus { | |
| 47 | + outline: none; | |
| 48 | +} | |
| 0 | 49 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -74,5 +74,8 @@ |
| 74 | 74 | "profile.content.success.message": "Profile saved!", |
| 75 | 75 | "custom_content.title": "Edit content", |
| 76 | 76 | "profile.custom_header.label": "Header", |
| 77 | - "profile.custom_footer.label": "Footer" | |
| 77 | + "profile.custom_footer.label": "Footer", | |
| 78 | + "search.results.summary": "{results, plural, one{result} other{# results}}", | |
| 79 | + "search.results.query.label": "Search for:", | |
| 80 | + "search.results.query.placeholder": "Search" | |
| 78 | 81 | } | ... | ... |
| ... | ... | @@ -58,7 +58,7 @@ |
| 58 | 58 | "article.remove.success.title": "Bom trabalho!", |
| 59 | 59 | "article.remove.success.message": "Artigo removido!", |
| 60 | 60 | "article.remove.confirmation.title": "Tem certeza?", |
| 61 | - "article.remove.confirmation.message": "Não será possível recuperar este artigo!", | |
| 61 | + "article.remove.confirmation.message": "Não será possível recuperar este artigo!", | |
| 62 | 62 | "article.basic_editor.visibility": "Visibilidade", |
| 63 | 63 | "article.basic_editor.visibility.public": "Público", |
| 64 | 64 | "article.basic_editor.visibility.private": "Privado", |
| ... | ... | @@ -74,5 +74,8 @@ |
| 74 | 74 | "profile.content.success.message": "Perfil salvo!", |
| 75 | 75 | "custom_content.title": "Editar conteúdo", |
| 76 | 76 | "profile.custom_header.label": "Cabeçalho", |
| 77 | - "profile.custom_footer.label": "Rodapé" | |
| 77 | + "profile.custom_footer.label": "Rodapé", | |
| 78 | + "search.results.summary": "{results, plural, one{# resultado} other{# resultados}}", | |
| 79 | + "search.results.query.label": "Buscar:", | |
| 80 | + "search.results.query.placeholder": "Informe aqui sua busca" | |
| 78 | 81 | } | ... | ... |
| ... | ... | @@ -23,7 +23,7 @@ describe("Services", () => { |
| 23 | 23 | it("should remove article", (done) => { |
| 24 | 24 | let articleId = 1; |
| 25 | 25 | $httpBackend.expectDELETE(`/api/v1/articles/${articleId}`).respond(200, { success: "true" }); |
| 26 | - articleService.remove(<noosfero.Article>{id: articleId}); | |
| 26 | + articleService.remove(<noosfero.Article>{ id: articleId }); | |
| 27 | 27 | $httpBackend.flush(); |
| 28 | 28 | $httpBackend.verifyNoOutstandingExpectation(); |
| 29 | 29 | done(); |
| ... | ... | @@ -32,7 +32,7 @@ describe("Services", () => { |
| 32 | 32 | it("should return article children", (done) => { |
| 33 | 33 | let articleId = 1; |
| 34 | 34 | $httpBackend.expectGET(`/api/v1/articles/${articleId}/children`).respond(200, { articles: [{ name: "article1" }] }); |
| 35 | - articleService.getChildren(<noosfero.Article>{id: articleId}).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 35 | + articleService.getChildren(<noosfero.Article>{ id: articleId }).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 36 | 36 | expect(result.data).toEqual([{ name: "article1" }]); |
| 37 | 37 | done(); |
| 38 | 38 | }); |
| ... | ... | @@ -42,7 +42,7 @@ describe("Services", () => { |
| 42 | 42 | it("should get articles by profile", (done) => { |
| 43 | 43 | let profileId = 1; |
| 44 | 44 | $httpBackend.expectGET(`/api/v1/profiles/${profileId}/articles`).respond(200, { articles: [{ name: "article1" }] }); |
| 45 | - articleService.getByProfile(<noosfero.Profile>{id: profileId}).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 45 | + articleService.getByProfile(<noosfero.Profile>{ id: profileId }).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 46 | 46 | expect(result.data).toEqual([{ name: "article1" }]); |
| 47 | 47 | done(); |
| 48 | 48 | }); |
| ... | ... | @@ -52,7 +52,7 @@ describe("Services", () => { |
| 52 | 52 | it("should get articles by profile with additional filters", (done) => { |
| 53 | 53 | let profileId = 1; |
| 54 | 54 | $httpBackend.expectGET(`/api/v1/profiles/${profileId}/articles?path=test`).respond(200, { articles: [{ name: "article1" }] }); |
| 55 | - articleService.getByProfile(<noosfero.Profile>{id: profileId}, { path: 'test' }).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 55 | + articleService.getByProfile(<noosfero.Profile>{ id: profileId }, { path: 'test' }).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 56 | 56 | expect(result.data).toEqual([{ name: "article1" }]); |
| 57 | 57 | done(); |
| 58 | 58 | }); |
| ... | ... | @@ -62,7 +62,7 @@ describe("Services", () => { |
| 62 | 62 | it("should get article children with additional filters", (done) => { |
| 63 | 63 | let articleId = 1; |
| 64 | 64 | $httpBackend.expectGET(`/api/v1/articles/${articleId}/children?path=test`).respond(200, { articles: [{ name: "article1" }] }); |
| 65 | - articleService.getChildren(<noosfero.Article>{id: articleId}, { path: 'test' }).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 65 | + articleService.getChildren(<noosfero.Article>{ id: articleId }, { path: 'test' }).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 66 | 66 | expect(result.data).toEqual([{ name: "article1" }]); |
| 67 | 67 | done(); |
| 68 | 68 | }); |
| ... | ... | @@ -71,14 +71,25 @@ describe("Services", () => { |
| 71 | 71 | |
| 72 | 72 | it("should create an article in a profile", (done) => { |
| 73 | 73 | let profileId = 1; |
| 74 | - let article: noosfero.Article = <any>{ id: null}; | |
| 75 | - $httpBackend.expectPOST(`/api/v1/profiles/${profileId}/articles`, { article: article }).respond(200, {article: { id: 2 }}); | |
| 76 | - articleService.createInProfile(<noosfero.Profile>{id: profileId}, article).then((result: noosfero.RestResult<noosfero.Article>) => { | |
| 74 | + let article: noosfero.Article = <any>{ id: null }; | |
| 75 | + $httpBackend.expectPOST(`/api/v1/profiles/${profileId}/articles`, { article: article }).respond(200, { article: { id: 2 } }); | |
| 76 | + articleService.createInProfile(<noosfero.Profile>{ id: profileId }, article).then((result: noosfero.RestResult<noosfero.Article>) => { | |
| 77 | 77 | expect(result.data).toEqual({ id: 2 }); |
| 78 | 78 | done(); |
| 79 | 79 | }); |
| 80 | 80 | $httpBackend.flush(); |
| 81 | 81 | }); |
| 82 | + | |
| 83 | + it("should search for articles in environment", (done) => { | |
| 84 | + let profileId = 1; | |
| 85 | + let article: noosfero.Article = <any>{ id: null }; | |
| 86 | + $httpBackend.expectGET(`/api/v1/search/article?query=query`).respond(200, { articles: [{ id: 2 }] }); | |
| 87 | + articleService.search({ query: 'query' }).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 88 | + expect(result.data).toEqual([{ id: 2 }]); | |
| 89 | + done(); | |
| 90 | + }); | |
| 91 | + $httpBackend.flush(); | |
| 92 | + }); | |
| 82 | 93 | }); |
| 83 | 94 | |
| 84 | 95 | ... | ... |
| ... | ... | @@ -118,6 +118,11 @@ export class ArticleService extends RestangularService<noosfero.Article> { |
| 118 | 118 | return this.listSubElements(<noosfero.Article>articleElement, "children", params); |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | + search(params: any): ng.IPromise<noosfero.RestResult<noosfero.Article[]>> { | |
| 122 | + let deferred = this.$q.defer<noosfero.RestResult<noosfero.Article[]>>(); | |
| 123 | + let restRequest = this.restangularService.all("search").customGET('article', params); | |
| 124 | + restRequest.then(this.getHandleSuccessFunction(deferred)).catch(this.getHandleErrorFunction(deferred)); | |
| 1 |
|
|
| 125 | + return deferred.promise; | |
| 126 | + } | |
| 121 | 127 | |
| 122 | 128 | } |
| 123 | - | ... | ... |
-
Reassigned to @mfdeveloper
-
Esse componente é realmente necessário? Talvez utilizar somente o componente
SearchComponentcom condições para renderizar o form ou a lista de resultados. É apenas uma sugestão, avalie com calma o que seria mais interessante.Me peguei a pensar sobre isso pq esse componente só renderiza um HTML diferente, e um único método que na verdade, redireciona para uma rota! :)
-
É necessário pois o form fica na navbar e o resultado da busca responde por uma rota específica.
-
Para mudar a quantidade de registros por página, não seria interessante coletar isso também pelo
$stateParams? -
Sim, fica melhor.
-
Para ficar mais claro e limpa essas condições, não seria melhor adicionar isso a um método do
SearchComponent? Pensando que um designer qualquer possa sobreescrever essa view em temas customizados, minimizar a quantidade de condições + especificas pode melhorar a legibilidade! -
Não entendi o que você quis dizer nesse comentário.
-
Algo nessa linha:
<div class="summary"> {{ ctrl.resultsText }} </div>Onde
ctrl.resultsTexté um atributo ou método get do typescript que já retorna o texto traduzido a depender da condição que você adicionou acima, entende? -
Se o resultado da pesquisa retornar 404 do endpoint do backend, é exibida uma página de Not found específica? Como é tratado os diferentes códigos de erro do servidor?
-
Essas cores não poderiam ser variáveis para serem reutilizadas e/ou sobrescritas em outros temas?
-
O campo de busca está desaparecendo no responsivo. O comportamento é este mesmo?
-
O texto
resultslogo abaixo do titulo no resultado da busca, não está traduzindo para pt-br quando modifico o idioma -
Assignee removed
-
mentioned in commit d0fa7823b63317f1649830850aeb477bcabf7b18
| 1 | +import {Component, Inject} from "ng-forward"; | |
| 2 | + | |
| 3 | +@Component({ | |
| 4 | + selector: 'search-form', | |
| 5 | + templateUrl: 'app/search/search-form/search-form.html' | |
| 6 | +}) | |
| 7 | +@Inject("$state") | |
| 8 | +export class SearchFormComponent { | |
| 2 |
|
|
| 1 | +import {Component, Inject} from "ng-forward"; | |
| 2 | +import {ArticleService} from "./../../lib/ng-noosfero-api/http/article.service"; | |
| 3 | + | |
| 4 | +@Component({ | |
| 5 | + selector: 'search', | |
| 6 | + templateUrl: 'app/search/search.html' | |
| 7 | +}) | |
| 8 | +@Inject(ArticleService, "$stateParams") | |
| 9 | +export class SearchComponent { | |
| 10 | + | |
| 11 | + articles: noosfero.Article[]; | |
| 12 | + query: string; | |
| 13 | + totalResults = 0; | |
| 14 | + perPage = 10; | |
| 2 |
|
|
| 1 | +<h2>Search</h2> | |
| 2 | + | |
| 3 | +<div class="search-results"> | |
| 4 | + <div class="summary"> | |
| 5 | + {{"search.results.summary" | translate:{results: ctrl.totalResults}:"messageformat"}} | |
| 3 |
|
|
| 118 | 118 | return this.listSubElements(<noosfero.Article>articleElement, "children", params); |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | + search(params: any): ng.IPromise<noosfero.RestResult<noosfero.Article[]>> { | |
| 122 | + let deferred = this.$q.defer<noosfero.RestResult<noosfero.Article[]>>(); | |
| 123 | + let restRequest = this.restangularService.all("search").customGET('article', params); | |
| 124 | + restRequest.then(this.getHandleSuccessFunction(deferred)).catch(this.getHandleErrorFunction(deferred)); | |
| 1 |
|
|
| 2 | + .summary { | |
| 3 | + color: #bbbbbb; | |
| 4 | + font-size: 13px; | |
| 5 | + border-top: 1px solid #ececec; | |
| 6 | + } | |
| 7 | + .result { | |
| 8 | + margin: 25px 0; | |
| 9 | + | |
| 10 | + .title { | |
| 11 | + h4 { | |
| 12 | + margin: 0; | |
| 13 | + } | |
| 14 | + } | |
| 15 | + .info { | |
| 16 | + .profile { | |
| 17 | + color: #6e9e7b; | |
| 1 |
|
|