Merge lp:~jcsackett/juju-gui/search-view into lp:juju-gui/experimental
- search-view
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 598 |
Proposed branch: | lp:~jcsackett/juju-gui/search-view |
Merge into: | lp:juju-gui/experimental |
Diff against target: |
785 lines (+371/-91) 16 files modified
app/modules-debug.js (+10/-2) app/subapps/browser/browser.js (+43/-13) app/subapps/browser/templates/search.handlebars (+4/-0) app/subapps/browser/views/charm.js (+2/-13) app/subapps/browser/views/editorial.js (+14/-15) app/subapps/browser/views/search.js (+110/-0) app/subapps/browser/views/utils.js (+43/-0) app/subapps/browser/views/view.js (+14/-14) app/templates/browser-search.handlebars (+1/-1) app/widgets/charm-search.js (+23/-7) test/index.html (+10/-8) test/test_browser_app.js (+4/-4) test/test_browser_charm_details.js (+0/-1) test/test_browser_search_view.js (+72/-0) test/test_browser_search_widget.js (+2/-13) test/test_browser_view_utils.js (+19/-0) |
To merge this branch: | bzr merge lp:~jcsackett/juju-gui/search-view |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju GUI Hackers | Pending | ||
Review via email: mp+159493@code.launchpad.net |
Commit message
Description of the change
Add search view.
SearchView is pretty basic; it knows its search text, and can query for it via
the Charmworld0 api.
It renders results out to a container, passed in at render.
It requeries and renders when the text changes.
j.c.sackett (jcsackett) wrote : | # |
Richard Harding (rharding) wrote : | # |
This branch seems to be missing the tie in for the text input. The
re-search is driven by the ATTR on the view, but getting that text into
there is missing. I'd like to chat and see what the plan is.
Comments below, but take those with a grain of salt as I'm not sure I
follow.
https:/
File app/subapps/
https:/
app/subapps/
this can go away if you move the ajax call per the later comment.
https:/
app/subapps/
move this logic into the render and just assume you're always given a
container here. That way the logic isn't split in two places.
https:/
app/subapps/
Can we move this back up to render as is done in the CharmView here:
http://
This way the _renderSearchRe
results handed to it.
https:/
app/subapps/
you can pass the scope as the final parameter to after so that you don't
need to deal with the currentTarget business. Just
this.get(
https:/
File test/test_
https:/
test/test_
id="container"
so the container should come from the View and then get appended to
something. I was trying to clean this up in
https:/
The way Y.View is meant to work (and I've been doing this wrong) is that
it defaults to an empty <div> as a container and then your render call
is responsible for putting it somewhere on the DOM.
- 559. By j.c.sackett
-
Merged search-routing into search-view.
- 560. By j.c.sackett
-
Merged search-routing into search-view.
- 561. By j.c.sackett
-
Merged search-routing into search-view.
- 562. By j.c.sackett
-
Merged api-faiure-util into search-view.
- 563. By j.c.sackett
-
apiFailure driveby
- 564. By j.c.sackett
-
Search results rendering.
- 565. By j.c.sackett
-
Test fix.
- 566. By j.c.sackett
-
Test fixes.
- 567. By j.c.sackett
-
Resolved conflicts.
- 568. By j.c.sackett
-
Add apiFailure utils req.
- 569. By j.c.sackett
-
Merged api-faiure-util into search-view.
- 570. By j.c.sackett
-
Revert utils req; uneeded, breaks devel.
- 571. By j.c.sackett
-
Shut up, lint.
- 572. By j.c.sackett
-
No really, shut up, lint.
- 573. By j.c.sackett
-
Documentation.
- 574. By j.c.sackett
-
More lint.
Preview Diff
1 | === modified file 'app/modules-debug.js' |
2 | --- app/modules-debug.js 2013-04-13 00:18:21 +0000 |
3 | +++ app/modules-debug.js 2013-04-22 13:35:38 +0000 |
4 | @@ -288,6 +288,10 @@ |
5 | fullpath: '/juju-ui/subapps/browser/views/fullscreen.js' |
6 | }, |
7 | |
8 | + 'subapp-browser-searchview': { |
9 | + fullpath: '/juju-ui/subapps/browser/views/search.js' |
10 | + }, |
11 | + |
12 | 'subapp-browser-sidebar': { |
13 | fullpath: '/juju-ui/subapps/browser/views/sidebar.js', |
14 | requires: [ |
15 | @@ -298,8 +302,12 @@ |
16 | }, |
17 | |
18 | 'subapp-browser-editorial': { |
19 | - fullpath: '/juju-ui/subapps/browser/views/editorial.js', |
20 | - requires: ['subapp-browser-sidebar'] |
21 | + fullpath: '/juju-ui/subapps/browser/views/editorial.js' |
22 | + }, |
23 | + |
24 | + //Browser view utils |
25 | + 'subapp-browser-view-utils': { |
26 | + fullpath: '/juju-ui/subapps/browser/views/utils.js' |
27 | }, |
28 | |
29 | //Browser Models |
30 | |
31 | === modified file 'app/subapps/browser/browser.js' |
32 | --- app/subapps/browser/browser.js 2013-04-19 17:58:14 +0000 |
33 | +++ app/subapps/browser/browser.js 2013-04-22 13:35:38 +0000 |
34 | @@ -63,9 +63,14 @@ |
35 | if (this._viewState.charmID) { |
36 | urlParts.push(this._viewState.charmID); |
37 | } |
38 | - |
39 | - // Always end on a / |
40 | - return urlParts.join('/'); |
41 | + var url = urlParts.join('/'); |
42 | + if (this._viewState.querystring) { |
43 | + url = Y.Lang.sub('{ url }?{ qs }', { |
44 | + url: url, |
45 | + qs: this._viewState.querystring |
46 | + }); |
47 | + } |
48 | + return url; |
49 | }, |
50 | |
51 | /** |
52 | @@ -201,7 +206,7 @@ |
53 | this._viewState.viewmode = params.viewmode; |
54 | |
55 | // Check for a charm id in the request. |
56 | - if (params.id) { |
57 | + if (params.id && params.id !== 'search') { |
58 | this._viewState.charmID = params.id; |
59 | } else { |
60 | this._viewState.charmID = null; |
61 | @@ -268,9 +273,9 @@ |
62 | }, |
63 | |
64 | /** |
65 | - * Render the sidebar view of a specific charm to the client. |
66 | + * Render the charm details view |
67 | * |
68 | - * @method sidebarCharm |
69 | + * @method renderCharmDetails |
70 | * @param {Request} req current request object. |
71 | * @param {Response} res current response object. |
72 | * @param {function} next callable for the next route in the chain. |
73 | @@ -317,7 +322,6 @@ |
74 | renderEditorial: function(req, res, next) { |
75 | // If loading the interesting content then it's not a search going on. |
76 | var container = this.get('container'), |
77 | - editorialContainer, |
78 | extraCfg = {}; |
79 | |
80 | if (this._viewState.viewmode === 'fullscreen') { |
81 | @@ -346,11 +350,35 @@ |
82 | }, |
83 | |
84 | /** |
85 | - Place holder for a method to render out search so we can test url parsing |
86 | + * Render search results |
87 | + * |
88 | + * @method renderSearch |
89 | + * @param {Request} req current request object. |
90 | + * @param {Response} res current response object. |
91 | + * @param {function} next callable for the next route in the chain. |
92 | + */ |
93 | + renderSearch: function(req, res, next) { |
94 | + var container = this.get('container'), |
95 | + extraCfg = {}, |
96 | + query; |
97 | + if (this._viewState.querystring) { |
98 | + query = Y.QueryString.parse(); |
99 | + } else { |
100 | + // If there's no querystring, we need a default "empty" search. |
101 | + query = {text: ''}; |
102 | + } |
103 | |
104 | - */ |
105 | - renderSearchResults: function(req, res, next) { |
106 | - console.log('rendered search results.'); |
107 | + if (this._viewState.viewmode === 'fullscreen') { |
108 | + extraCfg.renderTo = container.one('.bws-view-data'); |
109 | + extraCfg.isFullscreen = true; |
110 | + } else { |
111 | + extraCfg.renderTo = container.one('.bws-content'); |
112 | + } |
113 | + extraCfg.text = query.text; |
114 | + this._search = new Y.juju.browser.views.BrowserSearchView( |
115 | + this._getViewCfg(extraCfg)); |
116 | + this._search.render(); |
117 | + this._search.addTarget(this); |
118 | }, |
119 | |
120 | /** |
121 | @@ -379,7 +407,7 @@ |
122 | } else if (this._shouldShowSearch()) { |
123 | // Render search results if search is in the url and the viewmode or |
124 | // the search has been changed in the state. |
125 | - this.renderSearchResults(req, res, next); |
126 | + this.renderSearch(req, res, next); |
127 | } else if (!this._viewState.charmID) { |
128 | // Render the editorial in fullscreen only if we don't have a charmid |
129 | this.renderEditorial(req, res, next); |
130 | @@ -409,7 +437,7 @@ |
131 | // Render search results if search is in the url and the viewmode or the |
132 | // search has been changed in the state. |
133 | if (this._shouldShowSearch()) { |
134 | - this.renderSearchResults(req, res, next); |
135 | + this.renderSearch(req, res, next); |
136 | } |
137 | |
138 | if (this._shouldShowEditorial()) { |
139 | @@ -531,10 +559,12 @@ |
140 | requires: [ |
141 | 'juju-charm-store', |
142 | 'juju-models', |
143 | + 'querystring-parse', |
144 | 'sub-app', |
145 | 'subapp-browser-charmview', |
146 | 'subapp-browser-editorial', |
147 | 'subapp-browser-fullscreen', |
148 | + 'subapp-browser-searchview', |
149 | 'subapp-browser-sidebar' |
150 | ] |
151 | }); |
152 | |
153 | === added file 'app/subapps/browser/templates/search.handlebars' |
154 | --- app/subapps/browser/templates/search.handlebars 1970-01-01 00:00:00 +0000 |
155 | +++ app/subapps/browser/templates/search.handlebars 2013-04-22 13:35:38 +0000 |
156 | @@ -0,0 +1,4 @@ |
157 | +<div id="bws-search"> |
158 | + <h3>{{ count }} results found</h3> |
159 | + <div class="search-results"></div> |
160 | +</div> |
161 | |
162 | === modified file 'app/subapps/browser/views/charm.js' |
163 | --- app/subapps/browser/views/charm.js 2013-04-19 15:51:06 +0000 |
164 | +++ app/subapps/browser/views/charm.js 2013-04-22 13:35:38 +0000 |
165 | @@ -68,19 +68,7 @@ |
166 | * |
167 | */ |
168 | apiFailure: function(data, request) { |
169 | - var message; |
170 | - if (data && data.type) { |
171 | - message = 'Charm API error of type: ' + data.type; |
172 | - } else { |
173 | - message = 'Charm API server did not respond'; |
174 | - } |
175 | - this.get('db').notifications.add( |
176 | - new models.Notification({ |
177 | - title: 'Failed to load sidebar content.', |
178 | - message: message, |
179 | - level: 'error' |
180 | - }) |
181 | - ); |
182 | + Y.juju.browser.views.utils.apiFailure(data, request, this); |
183 | }, |
184 | |
185 | /** |
186 | @@ -561,6 +549,7 @@ |
187 | 'juju-view-utils', |
188 | 'node', |
189 | 'prettify', |
190 | + 'subapp-browser-view-utils', |
191 | 'view' |
192 | ] |
193 | }); |
194 | |
195 | === modified file 'app/subapps/browser/views/editorial.js' |
196 | --- app/subapps/browser/views/editorial.js 2013-04-19 17:09:07 +0000 |
197 | +++ app/subapps/browser/views/editorial.js 2013-04-22 13:35:38 +0000 |
198 | @@ -51,6 +51,17 @@ |
199 | }, |
200 | |
201 | /** |
202 | + * Generates a message to the user based on a bad api call. |
203 | + * |
204 | + * @method apiFailure |
205 | + * @param {Object} data the json decoded response text. |
206 | + * @param {Object} request the original io_request object for debugging. |
207 | + * |
208 | + */ |
209 | + apiFailure: function(data, request) { |
210 | + Y.juju.browser.views.utils.apiFailure(data, request, this); |
211 | + }, |
212 | + /** |
213 | * General YUI initializer. |
214 | * |
215 | * @method initializer |
216 | @@ -131,24 +142,11 @@ |
217 | ]; |
218 | }, |
219 | |
220 | - 'failure': function(data, request) { |
221 | - var message; |
222 | - if (data && data.type) { |
223 | - message = 'Charm API error of type: ' + data.type; |
224 | - } else { |
225 | - message = 'Charm API server did not respond'; |
226 | - } |
227 | - this.get('db').notifications.add( |
228 | - new models.Notification({ |
229 | - title: 'Failed to load landing page content.', |
230 | - message: message, |
231 | - level: 'error' |
232 | - }) |
233 | - ); |
234 | - } |
235 | + 'failure': this.apiFailure |
236 | }, this); |
237 | }, |
238 | |
239 | + /* |
240 | /** |
241 | * Destroy this view and clear from the dom world. |
242 | * |
243 | @@ -210,6 +208,7 @@ |
244 | 'juju-charm-store', |
245 | 'juju-models', |
246 | 'juju-templates', |
247 | + 'subapp-browser-view-utils', |
248 | 'view' |
249 | ] |
250 | }); |
251 | |
252 | === added file 'app/subapps/browser/views/search.js' |
253 | --- app/subapps/browser/views/search.js 1970-01-01 00:00:00 +0000 |
254 | +++ app/subapps/browser/views/search.js 2013-04-22 13:35:38 +0000 |
255 | @@ -0,0 +1,110 @@ |
256 | +'use strict'; |
257 | + |
258 | + |
259 | +/** |
260 | + * Provides searching functionality for the charm browser. |
261 | + * |
262 | + * @namespace juju |
263 | + * @module browser |
264 | + * @submodule views |
265 | + */ |
266 | +YUI.add('subapp-browser-searchview', function(Y) { |
267 | + var ns = Y.namespace('juju.browser.views'), |
268 | + views = Y.namespace('juju.views'), |
269 | + widgets = Y.namespace('juju.widgets'), |
270 | + models = Y.namespace('juju.models'); |
271 | + |
272 | + ns.BrowserSearchView = Y.Base.create('browser-view-searchview', Y.View, [ |
273 | + Y.Event.EventTracker |
274 | + ], { |
275 | + template: views.Templates.search, |
276 | + /** |
277 | + * Renders the search results from the the store query. |
278 | + * |
279 | + * @method _renderSearchResults |
280 | + * @param {Y.Node} container Optional container to render results to. |
281 | + */ |
282 | + _renderSearchResults: function(results) { |
283 | + var target = this.get('renderTo'), |
284 | + tpl = this.template({count: results.size()}), |
285 | + tplNode = Y.Node.create(tpl), |
286 | + container = tplNode.one('.search-results'); |
287 | + |
288 | + results.map(function(charm) { |
289 | + var ct = new widgets.browser.CharmToken(charm.getAttrs()); |
290 | + ct.render(container); |
291 | + }); |
292 | + target.setHTML(tplNode); |
293 | + }, |
294 | + |
295 | + /** |
296 | + * Generates a message to the user based on a bad api call. |
297 | + * |
298 | + * @method apiFailure |
299 | + * @param {Object} data the json decoded response text. |
300 | + * @param {Object} request the original io_request object for debugging. |
301 | + * |
302 | + */ |
303 | + apiFailure: function(data, request) { |
304 | + Y.juju.browser.views.utils.apiFailure(data, request, this); |
305 | + }, |
306 | + |
307 | + /** |
308 | + * Renders the searchview, rendering search results for the view's search |
309 | + * text. |
310 | + * |
311 | + * @method render |
312 | + */ |
313 | + render: function() { |
314 | + var text = this.get('text'); |
315 | + this.get('store').search(text, { |
316 | + 'success': function(data) { |
317 | + var results = this.get('store').resultsToCharmlist(data.result); |
318 | + this._renderSearchResults(results); |
319 | + }, |
320 | + 'failure': this.apiFailure |
321 | + }, this); |
322 | + } |
323 | + }, { |
324 | + ATTRS: { |
325 | + /** |
326 | + * The container node the view is rendering to. |
327 | + * |
328 | + * @attribute renderTo |
329 | + * @default undefined |
330 | + * @type {Y.Node} |
331 | + */ |
332 | + renderTo: {}, |
333 | + |
334 | + /** |
335 | + * An instance of the Charmworld API object to hit for any data that |
336 | + * needs fetching. |
337 | + * |
338 | + * @attribute store |
339 | + * @default undefined |
340 | + * @type {Charmworld0} |
341 | + * |
342 | + */ |
343 | + store: {}, |
344 | + |
345 | + /** |
346 | + * The text being searched on |
347 | + * |
348 | + * @attribute text |
349 | + * @default '' |
350 | + * @type {String} |
351 | + */ |
352 | + text: {} |
353 | + } |
354 | + }); |
355 | + |
356 | +}, '0.1.0', { |
357 | + requires: [ |
358 | + 'event-tracker', |
359 | + 'browser-overlay-indicator', |
360 | + 'base-build', |
361 | + 'browser-charm-token', |
362 | + 'subapp-browser-view-utils', |
363 | + 'view' |
364 | + ] |
365 | +}); |
366 | |
367 | === added file 'app/subapps/browser/views/utils.js' |
368 | --- app/subapps/browser/views/utils.js 1970-01-01 00:00:00 +0000 |
369 | +++ app/subapps/browser/views/utils.js 2013-04-22 13:35:38 +0000 |
370 | @@ -0,0 +1,43 @@ |
371 | +'use strict'; |
372 | + |
373 | + |
374 | +/** |
375 | + * The view utils. |
376 | + * |
377 | + * @namespace juju |
378 | + * @module juju.browser.views |
379 | + * @submodule juju.browser.views.utils |
380 | + */ |
381 | +YUI.add('subapp-browser-view-utils', function(Y) { |
382 | + |
383 | + var ns = Y.namespace('juju.browser.views.utils'), |
384 | + models = Y.namespace('juju.models'); |
385 | + |
386 | + /** |
387 | + * Shared method to generate a message to the user based on a bad api |
388 | + * call from a view. |
389 | + * |
390 | + * @method apiFailure |
391 | + * @param {Object} data the json decoded response text. |
392 | + * @param {Object} request the original io_request object for debugging. |
393 | + * |
394 | + */ |
395 | + ns.apiFailure = function(data, request, view) { |
396 | + var message; |
397 | + if (data && data.type) { |
398 | + message = 'Charm API error of type: ' + data.type; |
399 | + } else { |
400 | + message = 'Charm API server did not respond'; |
401 | + } |
402 | + view.get('db').notifications.add( |
403 | + new models.Notification({ |
404 | + title: 'Failed to load sidebar content.', |
405 | + message: message, |
406 | + level: 'error' |
407 | + }) |
408 | + ); |
409 | + }; |
410 | + |
411 | +}, '0.1.0', { |
412 | + requires: [] |
413 | +}); |
414 | |
415 | === modified file 'app/subapps/browser/views/view.js' |
416 | --- app/subapps/browser/views/view.js 2013-04-16 23:39:54 +0000 |
417 | +++ app/subapps/browser/views/view.js 2013-04-22 13:35:38 +0000 |
418 | @@ -107,7 +107,17 @@ |
419 | * |
420 | */ |
421 | _searchChanged: function(ev) { |
422 | - console.log('search changed.'); |
423 | + // NB: This is temporary; eventually filtering will include categories, |
424 | + // and the Filter object will handle qs generation. But it's an unwieldy |
425 | + // url to parse while we only support text search. |
426 | + var qs = Y.QueryString.stringify({text: ev.details[0]}); |
427 | + var change = { |
428 | + search: true, |
429 | + querystring: qs |
430 | + }; |
431 | + this.fire('viewNavigate', { |
432 | + change: change |
433 | + }); |
434 | }, |
435 | |
436 | /** |
437 | @@ -160,19 +170,7 @@ |
438 | * |
439 | */ |
440 | apiFailure: function(data, request) { |
441 | - var message; |
442 | - if (data && data.type) { |
443 | - message = 'Charm API error of type: ' + data.type; |
444 | - } else { |
445 | - message = 'Charm API server did not respond'; |
446 | - } |
447 | - this.get('db').notifications.add( |
448 | - new models.Notification({ |
449 | - title: 'Failed to load sidebar content.', |
450 | - message: message, |
451 | - level: 'error' |
452 | - }) |
453 | - ); |
454 | + Y.juju.browser.views.utils.apiFaiure(data, request, this); |
455 | }, |
456 | |
457 | /** |
458 | @@ -269,6 +267,8 @@ |
459 | 'event-tracker', |
460 | 'juju-charm-store', |
461 | 'juju-models', |
462 | + 'querystring-stringify', |
463 | + 'subapp-browser-view-utils', |
464 | 'view' |
465 | ] |
466 | }); |
467 | |
468 | === modified file 'app/templates/browser-search.handlebars' |
469 | --- app/templates/browser-search.handlebars 2013-04-16 21:58:19 +0000 |
470 | +++ app/templates/browser-search.handlebars 2013-04-22 13:35:38 +0000 |
471 | @@ -6,7 +6,7 @@ |
472 | <div class="bws-searchbox"> |
473 | <form> |
474 | <input type="search" name="bws-search" |
475 | - value="{{ term }}" |
476 | + value="{{ text }}" |
477 | placeholder="Search…"/> |
478 | </form> |
479 | </div> |
480 | |
481 | === modified file 'app/widgets/charm-search.js' |
482 | --- app/widgets/charm-search.js 2013-04-09 17:00:45 +0000 |
483 | +++ app/widgets/charm-search.js 2013-04-22 13:35:38 +0000 |
484 | @@ -1,8 +1,7 @@ |
485 | 'use strict'; |
486 | |
487 | |
488 | -/** |
489 | - * The widget used across Browser view to manage the search box and the |
490 | +/** * The widget used across Browser view to manage the search box and the |
491 | * controls for selecting which view you're in. |
492 | * |
493 | * @module widgets |
494 | @@ -34,6 +33,17 @@ |
495 | TEMPLATE: templates['browser-search'], |
496 | |
497 | /** |
498 | + * Halt page reload from form submit and let the app know we have a new |
499 | + * search. |
500 | + * |
501 | + * @method _handleSubmit |
502 | + * @param {Event} ev the submit event. |
503 | + */ |
504 | + _handleSubmit: function(ev) { |
505 | + ev.halt(); |
506 | + this.fire(this.EVT_UPDATE_SEARCH, this.get('text')); |
507 | + }, |
508 | + /** |
509 | * Expose to the outside world that we've got a request to go fullscreen. |
510 | * |
511 | * @method _toggleFullScreen |
512 | @@ -76,6 +86,10 @@ |
513 | container.one('.toggle-fullscreen').on( |
514 | 'click', this._toggleFullScreen, this) |
515 | ); |
516 | + this.addEvent( |
517 | + container.one('form').on( |
518 | + 'submit', this._handleSubmit, this) |
519 | + ); |
520 | |
521 | // Note that the search could be updated either from our internal input |
522 | // control, or it could come from someone outside of the widget asking |
523 | @@ -84,6 +98,8 @@ |
524 | var input = container.one('input'); |
525 | this.addEvent( |
526 | input.on('valueChange', function(ev) { |
527 | + var val = ev.currentTarget.get('value'); |
528 | + this.set('text', val); |
529 | this.fire(this.EVT_SEARCH_CHANGED); |
530 | }, this) |
531 | ); |
532 | @@ -155,13 +171,13 @@ |
533 | }, |
534 | |
535 | /** |
536 | - * @attribute term |
537 | - * @default undefined |
538 | + * The search text. |
539 | + * |
540 | + * @attribute text |
541 | + * @default '' |
542 | * @type {String} |
543 | - * |
544 | */ |
545 | - term: {} |
546 | - |
547 | + text: {} |
548 | } |
549 | }); |
550 | |
551 | |
552 | === modified file 'test/index.html' |
553 | --- test/index.html 2013-04-10 11:57:11 +0000 |
554 | +++ test/index.html 2013-04-22 13:35:38 +0000 |
555 | @@ -30,36 +30,38 @@ |
556 | |
557 | <!-- Tests (Alphabetical)--> |
558 | |
559 | + <script src="test_app_hotkeys.js"></script> |
560 | <script src="test_app.js"></script> |
561 | - <script src="test_app_hotkeys.js"></script> |
562 | <script src="test_application_notifications.js"></script> |
563 | <script src="test_browser_app.js"></script> |
564 | <script src="test_browser_charm_details.js"></script> |
565 | <script src="test_browser_models.js"></script> |
566 | + <script src="test_browser_search_view.js"></script> |
567 | + <script src="test_browser_search_widget.js"></script> |
568 | + <script src="test_browser_view_utils.js"></script> |
569 | <script src="test_charm_collection_view.js"></script> |
570 | <script src="test_charm_configuration.js"></script> |
571 | <script src="test_charm_container.js"></script> |
572 | <script src="test_charm_panel.js"></script> |
573 | + <script src="test_charm_store.js"></script> |
574 | <script src="test_charm_token.js"></script> |
575 | - <script src="test_browser_search_widget.js"></script> |
576 | - <script src="test_charm_store.js"></script> |
577 | <script src="test_charm_view.js"></script> |
578 | <script src="test_console.js"></script> |
579 | <script src="test_d3_components.js"></script> |
580 | <script src="test_endpoints.js"></script> |
581 | + <script src="test_env_go.js"></script> |
582 | + <script src="test_environment_view.js"></script> |
583 | <script src="test_env.js"></script> |
584 | + <script src="test_env_python.js"></script> |
585 | <script src="test_event_tracker.js"></script> |
586 | - <script src="test_env_go.js"></script> |
587 | - <script src="test_env_python.js"></script> |
588 | - <script src="test_environment_view.js"></script> |
589 | <script src="test_fakebackend.js"></script> |
590 | - <script src="test_overlay_indicator.js"></script> |
591 | <script src="test_landscape.js"></script> |
592 | <script src="test_login.js"></script> |
593 | + <script src="test_model_handlers.js"></script> |
594 | <script src="test_model.js"></script> |
595 | - <script src="test_model_handlers.js"></script> |
596 | <script src="test_notifications.js"></script> |
597 | <script src="test_notifier_widget.js"></script> |
598 | + <script src="test_overlay_indicator.js"></script> |
599 | <script src="test_panzoom.js"></script> |
600 | <script src="test_prettify.js"></script> |
601 | <script src="test_routing.js"></script> |
602 | |
603 | === modified file 'test/test_browser_app.js' |
604 | --- test/test_browser_app.js 2013-04-19 17:59:38 +0000 |
605 | +++ test/test_browser_app.js 2013-04-22 13:35:38 +0000 |
606 | @@ -175,7 +175,7 @@ |
607 | sidebar: false, |
608 | renderCharmDetails: false, |
609 | renderEditorial: false, |
610 | - renderSearchResults: false |
611 | + renderSearch: false |
612 | }; |
613 | }; |
614 | done(); |
615 | @@ -213,8 +213,8 @@ |
616 | browser.renderEditorial = function() { |
617 | hits.renderEditorial = true; |
618 | }; |
619 | - browser.renderSearchResults = function() { |
620 | - hits.renderSearchResults = true; |
621 | + browser.renderSearch = function() { |
622 | + hits.renderSearch = true; |
623 | }; |
624 | // showView needs to be hacked because it does the rendering of |
625 | // fullscreen/sidebar. |
626 | @@ -272,7 +272,7 @@ |
627 | }; |
628 | var expected = Y.merge(hits, { |
629 | sidebar: true, |
630 | - renderSearchResults: true, |
631 | + renderSearch: true, |
632 | renderCharmDetails: true |
633 | }); |
634 | |
635 | |
636 | === modified file 'test/test_browser_charm_details.js' |
637 | --- test/test_browser_charm_details.js 2013-04-18 05:01:43 +0000 |
638 | +++ test/test_browser_charm_details.js 2013-04-22 13:35:38 +0000 |
639 | @@ -46,7 +46,6 @@ |
640 | delete window.juju_config; |
641 | }); |
642 | |
643 | - // Ensure the search results are rendered inside the container. |
644 | it('should be able to locate a readme file', function() { |
645 | view = new CharmView({ |
646 | charm: new models.BrowserCharm({ |
647 | |
648 | === added file 'test/test_browser_search_view.js' |
649 | --- test/test_browser_search_view.js 1970-01-01 00:00:00 +0000 |
650 | +++ test/test_browser_search_view.js 2013-04-22 13:35:38 +0000 |
651 | @@ -0,0 +1,72 @@ |
652 | +'use strict'; |
653 | + |
654 | + |
655 | +describe('search view', function() { |
656 | + var apiURL, |
657 | + container, |
658 | + view, |
659 | + Y; |
660 | + |
661 | + before(function(done) { |
662 | + Y = YUI(GlobalConfig).use( |
663 | + 'json', |
664 | + 'juju-charm-store', |
665 | + 'node', |
666 | + 'subapp-browser-searchview', |
667 | + function(Y) { |
668 | + done(); |
669 | + }); |
670 | + }); |
671 | + |
672 | + beforeEach(function() { |
673 | + // Mock out a dummy location for the Store used in view instances. |
674 | + window.juju_config = {charmworldURL: 'http://localhost'}; |
675 | + container = Y.Node.create('<div id="container"></div>'); |
676 | + Y.one('body').append(container); |
677 | + view = new Y.juju.browser.views.BrowserSearchView({text: 'foo'}); |
678 | + // |
679 | + // Create monkeypatched store to verify right method is called. |
680 | + apiURL = ''; |
681 | + var fakeStore = new Y.juju.Charmworld0({}); |
682 | + var sampleData = { |
683 | + result: [{ |
684 | + id: 'foo/bar-2', |
685 | + name: 'bar', |
686 | + description: 'some charm named bar' |
687 | + }] |
688 | + }; |
689 | + fakeStore.set('datasource', { |
690 | + sendRequest: function(params) { |
691 | + // Stubbing the server callback value |
692 | + apiURL = params.request; |
693 | + params.callback.success({ |
694 | + response: { |
695 | + results: [{ |
696 | + responseText: Y.JSON.stringify(sampleData) |
697 | + }] |
698 | + } |
699 | + }); |
700 | + } |
701 | + }); |
702 | + view.set('store', fakeStore); |
703 | + view.set('renderTo', container); |
704 | + }); |
705 | + |
706 | + afterEach(function() { |
707 | + delete window.juju_config; |
708 | + view.destroy(); |
709 | + container.remove(true); |
710 | + }); |
711 | + |
712 | + it('exists', function() { |
713 | + assert.isObject(view); |
714 | + }); |
715 | + |
716 | + it('renders correctly', function() { |
717 | + view.render(); |
718 | + assert.equal('charms?text=foo', apiURL); |
719 | + assert.equal(1, Y.all('.yui3-charmtoken').size()); |
720 | + var charmText = Y.one('.yui3-charmtoken').one('.title').get('text'); |
721 | + assert.equal(charmText.replace(/\s+/g, ''), 'bar'); |
722 | + }); |
723 | +}); |
724 | |
725 | === modified file 'test/test_browser_search_widget.js' |
726 | --- test/test_browser_search_widget.js 2013-03-25 19:12:45 +0000 |
727 | +++ test/test_browser_search_widget.js 2013-04-22 13:35:38 +0000 |
728 | @@ -44,6 +44,7 @@ |
729 | // now trigger the event and make sure that it fired to our custom |
730 | // watcher outside the widget. |
731 | triggered.should.eql(true); |
732 | + assert.equal('test', this.get('text')); |
733 | done(); |
734 | }); |
735 | |
736 | @@ -62,18 +63,7 @@ |
737 | |
738 | it('should supports clearing search string', function() { |
739 | var search = new Search({ |
740 | - term: 'test' |
741 | - }); |
742 | - search.render(container); |
743 | - container.one('input').get('value').should.eql('test'); |
744 | - |
745 | - search.clearSearch(); |
746 | - container.one('input').get('value').should.eql(''); |
747 | - }); |
748 | - |
749 | - it('should supports clearing search string', function() { |
750 | - var search = new Search({ |
751 | - term: 'test' |
752 | + text: 'test' |
753 | }); |
754 | search.render(container); |
755 | container.one('input').get('value').should.eql('test'); |
756 | @@ -109,5 +99,4 @@ |
757 | toggle.simulate('click'); |
758 | triggered.should.eql(true); |
759 | }); |
760 | - |
761 | }); |
762 | |
763 | === added file 'test/test_browser_view_utils.js' |
764 | --- test/test_browser_view_utils.js 1970-01-01 00:00:00 +0000 |
765 | +++ test/test_browser_view_utils.js 2013-04-22 13:35:38 +0000 |
766 | @@ -0,0 +1,19 @@ |
767 | +'use strict'; |
768 | + |
769 | +describe('api failure utility', function() { |
770 | + var Y; |
771 | + |
772 | + before(function(done) { |
773 | + Y = YUI(GlobalConfig).use('subapp-browser-view-utils', function(Y) { |
774 | + done(); |
775 | + }); |
776 | + }); |
777 | + |
778 | + beforeEach(function() {}); |
779 | + |
780 | + afterEach(function() {}); |
781 | + |
782 | + it('exists', function() { |
783 | + assert.isFunction(Y.juju.browser.views.utils.apiFailure); |
784 | + }); |
785 | +}); |
Reviewers: mp+159493_ code.launchpad. net,
Message:
Please take a look.
Description:
Add search view.
SearchView is pretty basic; it knows its search text, and can query for
it via
the Charmworld0 api.
It renders results out to a container, passed in at render.
It requeries and renders when the text changes.
https:/ /code.launchpad .net/~jcsackett /juju-gui/ search- view/+merge/ 159493
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/8835044/
Affected files: debug.js browser/ views/search. js browser_ charm_details. js browser_ search_ view.js
A [revision details]
M app/modules-
A app/subapps/
M test/index.html
M test/test_
A test/test_