Refactor search results to be components instead of views for reuse

This commit is contained in:
Robin Ward 2015-06-22 14:05:35 -04:00
parent 76bfd723f6
commit 7ed309666b
21 changed files with 764 additions and 81 deletions

View file

@ -0,0 +1,2 @@
import SearchResult from 'discourse/components/search-result';
export default SearchResult.extend();

View file

@ -0,0 +1,2 @@
import SearchResult from 'discourse/components/search-result';
export default SearchResult.extend();

View file

@ -0,0 +1,2 @@
import SearchResult from 'discourse/components/search-result';
export default SearchResult.extend();

View file

@ -0,0 +1,2 @@
import SearchResult from 'discourse/components/search-result';
export default SearchResult.extend();

View file

@ -0,0 +1,11 @@
export default Ember.Component.extend({
tagName: 'ul',
_highlightOnInsert: function() {
const term = this.get('controller.term');
if(!_.isEmpty(term)) {
this.$('.blurb').highlight(term.split(/\s+/), {className: 'search-highlight'});
this.$('.topic-title').highlight(term.split(/\s+/), {className: 'search-highlight'} );
}
}.on('didInsertElement')
});

View file

@ -0,0 +1,7 @@
import TextField from 'discourse/components/text-field';
export default TextField.extend({
placeholder: function() {
return this.get('searchContextEnabled') ? "" : I18n.t('search.title');
}.property('searchContextEnabled')
});

View file

@ -4,7 +4,7 @@ function searchForTerm(term, opts) {
if (!opts) opts = {}; if (!opts) opts = {};
// Only include the data we have // Only include the data we have
var data = { term: term, include_blurbs: 'true' }; const data = { term: term, include_blurbs: 'true' };
if (opts.typeFilter) data.type_filter = opts.typeFilter; if (opts.typeFilter) data.type_filter = opts.typeFilter;
if (opts.searchForId) data.search_for_id = true; if (opts.searchForId) data.search_for_id = true;
@ -22,7 +22,7 @@ function searchForTerm(term, opts) {
if (!results.posts) { results.posts = []; } if (!results.posts) { results.posts = []; }
if (!results.categories) { results.categories = []; } if (!results.categories) { results.categories = []; }
var topicMap = {}; const topicMap = {};
results.topics = results.topics.map(function(topic){ results.topics = results.topics.map(function(topic){
topic = Topic.create(topic); topic = Topic.create(topic);
topicMap[topic.id] = topic; topicMap[topic.id] = topic;
@ -44,23 +44,23 @@ function searchForTerm(term, opts) {
return Discourse.Category.list().findProperty('id', category.id); return Discourse.Category.list().findProperty('id', category.id);
}).compact(); }).compact();
var r = results.grouped_search_result; const r = results.grouped_search_result;
results.resultTypes = []; results.resultTypes = [];
// TODO: consider refactoring front end to take a better structure // TODO: consider refactoring front end to take a better structure
[['topic','posts'],['user','users'],['category','categories']].forEach(function(pair){ [['topic','posts'],['user','users'],['category','categories']].forEach(function(pair){
var type = pair[0], name = pair[1]; const type = pair[0], name = pair[1];
if(results[name].length > 0) { if (results[name].length > 0) {
results.resultTypes.push({ results.resultTypes.push({
results: results[name], results: results[name],
displayType: (opts.searchContext && opts.searchContext.type === 'topic' && type === 'topic') ? 'post' : type, componentName: "search-result-" + ((opts.searchContext && opts.searchContext.type === 'topic' && type === 'topic') ? 'post' : type),
type: type, type,
more: r['more_' + name] more: r['more_' + name]
}); });
} }
}); });
var noResults = !!(results.topics.length === 0 && const noResults = !!(results.topics.length === 0 &&
results.posts.length === 0 && results.posts.length === 0 &&
results.users.length === 0 && results.users.length === 0 &&
results.categories.length === 0); results.categories.length === 0);

View file

@ -0,0 +1,7 @@
{{#each results as |result|}}
<li>
<a href='{{unbound result.url}}'>
{{category-badge result}}
</a>
</li>
{{/each}}

View file

@ -0,0 +1,12 @@
{{#each results as |result|}}
<a class='search-link' href='{{unbound result.url}}'>
<span class='topic'>
{{i18n 'search.post_format' post_number=result.post_number username=result.username}}
</span>
{{#unless site.mobileView}}
<span class='blurb'>
{{{unbound result.blurb}}}
</span>
{{/unless}}
</a>
{{/each}}

View file

@ -0,0 +1,14 @@
{{#each results as |result|}}
<li>
<a class='search-link' href='{{unbound url}}'>
<span class='topic'>
{{topic-status topic=result.topic disableActions=true}}<span class='topic-title'>{{unbound result.topic.title}}</span>{{category-badge result.topic.category}}
</span>
{{#unless site.mobileView}}
<span class='blurb'>
{{format-age result.created_at}} - {{{unbound result.blurb}}}
</span>
{{/unless}}
</a>
</li>
{{/each}}

View file

@ -0,0 +1,8 @@
{{#each results as |result|}}
<li>
<a href='{{unbound result.path}}'>
{{avatar result imageSize="small"}}
{{unbound result.username}}
</a>
</li>
{{/each}}

View file

@ -1,4 +1,5 @@
{{view "search-text-field" value=term searchContextEnabled=searchContextEnabled searchContext=searchContext id="search-term"}} {{search-text-field value=term searchContextEnabled=searchContextEnabled id="search-term"}}
<div class="search-context"> <div class="search-context">
{{#if searchContext}} {{#if searchContext}}
<label> <label>
@ -16,10 +17,10 @@
{{i18n "search.no_results"}} {{i18n "search.no_results"}}
</div> </div>
{{else}} {{else}}
{{#each resultType in content.resultTypes}} {{#each content.resultTypes as |resultType|}}
<ul> <ul>
<li class="heading row">{{resultType.name}}</li> <li class="heading row">{{resultType.name}}</li>
{{view "search-results-type" type=resultType.type displayType=resultType.displayType content=resultType.results}} {{component resultType.componentName results=resultType.results term=term}}
</ul> </ul>
<div class="no-results"> <div class="no-results">
{{#if resultType.more}} {{#if resultType.more}}

View file

@ -1,3 +0,0 @@
<a href='{{unbound url}}'>
{{category-badge this}}
</a>

View file

@ -1,10 +0,0 @@
<a class='search-link' href='{{unbound url}}'>
<span class='topic'>
{{i18n 'search.post_format' post_number=post_number username=username}}
</span>
{{#unless controller.site.mobileView}}
<span class='blurb'>
{{{unbound blurb}}}
</span>
{{/unless}}
</a>

View file

@ -1,10 +0,0 @@
<a class='search-link' href='{{unbound url}}'>
<span class='topic'>
{{topic-status topic=topic disableActions=true}}<span class='topic-title'>{{unbound topic.title}}</span>{{category-badge topic.category}}
</span>
{{#unless controller.site.mobileView}}
<span class='blurb'>
{{format-age created_at}} - {{{unbound blurb}}}
</span>
{{/unless}}
</a>

View file

@ -1,4 +0,0 @@
<a href='{{unbound path}}'>
{{avatar this imageSize="small"}}
{{unbound username}}
</a>

View file

@ -1,15 +0,0 @@
export default Ember.CollectionView.extend({
tagName: 'ul',
itemViewClass: Discourse.GroupedView.extend({
tagName: 'li',
classNameBindings: ['selected'],
templateName: Discourse.computed.fmt('parentView.displayType', "search/%@_result")
}),
didInsertElement: function(){
var term = this.get('controller.term');
if(!_.isEmpty(term)) {
this.$('.blurb').highlight(term.split(/\s+/), {className: 'search-highlight'});
this.$('.topic-title').highlight(term.split(/\s+/), {className: 'search-highlight'} );
}
}
});

View file

@ -1,27 +0,0 @@
/**
This is a text field that supports a dynamic placeholder based on search context.
@class SearchTextField
@extends Discourse.TextField
@namespace Discourse
@module Discourse
**/
import TextField from 'discourse/components/text-field';
export default TextField.extend({
/**
A dynamic placeholder for the search field based on our context
@property placeholder
**/
placeholder: function() {
if(this.get('searchContextEnabled')){
return "";
}
return I18n.t('search.title');
}.property('searchContextEnabled')
});

View file

@ -45,6 +45,7 @@
//= require ./discourse/views/cloaked //= require ./discourse/views/cloaked
//= require ./discourse/components/combo-box //= require ./discourse/components/combo-box
//= require ./discourse/views/button //= require ./discourse/views/button
//= require ./discourse/components/search-result
//= require ./discourse/components/dropdown-button //= require ./discourse/components/dropdown-button
//= require ./discourse/components/notifications-button //= require ./discourse/components/notifications-button
//= require ./discourse/components/topic-notifications-button //= require ./discourse/components/topic-notifications-button

View file

@ -0,0 +1,18 @@
import { acceptance } from "helpers/qunit-helpers";
acceptance("Search");
test("search", (assert) => {
visit("/");
click('#search-button');
andThen(() => {
assert.ok(exists('#search-term'), 'it shows the search bar');
assert.ok(!exists('#search-dropdown .results ul li'), 'no results by default');
});
fillIn('#search-term', 'dev');
andThen(() => {
assert.ok(exists('#search-dropdown .results ul li'), 'it shows results');
});
});

File diff suppressed because one or more lines are too long