Merge lp:~jcsackett/juju-gui/view-controls-mixin into lp:juju-gui/experimental

Proposed by j.c.sackett
Status: Merged
Merged at revision: 974
Proposed branch: lp:~jcsackett/juju-gui/view-controls-mixin
Merge into: lp:juju-gui/experimental
Diff against target: 533 lines (+206/-189)
6 files modified
app/subapps/browser/views/minimized.js (+5/-69)
app/subapps/browser/views/view.js (+7/-53)
app/views/utils.js (+2/-1)
app/widgets/viewmode-controls.js (+72/-0)
test/test_browser_app.js (+48/-66)
test/test_viewmode_controls_widget.js (+72/-0)
To merge this branch: bzr merge lp:~jcsackett/juju-gui/view-controls-mixin
Reviewer Review Type Date Requested Status
Juju GUI Hackers Pending
Review via email: mp+181615@code.launchpad.net

Description of the change

Moves viewmode control binding to a mixin

* A new mixin has been created
* Nav functions like _goFullscreen have been moved to the mixin
* * toggleMinimized is just a placeholder, as fullscreen/sidebar need to do a
different thing than minimized
* The mixin doesn't create controls; it just has them passed in, since different
views setup the controls differently.

https://codereview.appspot.com/12935046/

To post a comment you must log in.
Revision history for this message
Richard Harding (rharding) wrote :

Thanks for the cleanup.

I'd suggest keeping the extension in the widget code base. It's tied to the widget and needs to work with it. The Views are already importing that module in order to display the widget. The extension comes along for free. Then the widget tests can consume the tests for the extension as well to verify things work as they should work tied together. It's a pattern setup by the overlay-indicator widget.

Naming-wise I'd suggest something like ViewModeControlsView(Extension|Helper) or something.

#264 - you pass in controls, but use this.controls to bind the events.

The tests used in the current views should probably be pulled into testing this exntension directly. You could create an empty Y.View class, stick the extension on it, and make sure that the events fire with the right payload as it done currently in testing fullscreen/sidebar. Then they can not be duped in individual fullscreen/minimized/sidebar views.

Revision history for this message
j.c.sackett (jcsackett) wrote :

Reviewers: mp+181615_code.launchpad.net,

Message:
Please take a look.

Description:
Moves viewmode control binding to a mixin

* A new mixin has been created
* Nav functions like _goFullscreen have been moved to the mixin
* * toggleMinimized is just a placeholder, as fullscreen/sidebar need to
do a
different thing than minimized
* The mixin doesn't create controls; it just has them passed in, since
different
views setup the controls differently.
* Tests haven't been moved; seeing that the functionality works in the
views
seemes more valueable than setting up the necessary mocks to tests the
mixin
alone.

https://code.launchpad.net/~jcsackett/juju-gui/view-controls-mixin/+merge/181615

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/12935046/

Affected files:
   A [revision details]
   M app/subapps/browser/views/minimized.js
   M app/subapps/browser/views/view.js
   M app/views/utils.js

Revision history for this message
Richard Harding (rharding) wrote :

Thanks for the cleanup.

I'd suggest keeping the extension in the widget code base. It's tied to
the widget and needs to work with it. The Views are already importing
that module in order to display the widget. The extension comes along
for free. Then the widget tests can consume the tests for the extension
as well to verify things work as they should work tied together. It's a
pattern setup by the overlay-indicator widget.

Naming-wise I'd suggest something like
ViewModeControlsView(Extension|Helper) or something.

The tests used in the current views should probably be pulled into
testing this exntension directly. You could create an empty Y.View
class, stick the extension on it, and make sure that the events fire
with the right payload as it done currently in testing
fullscreen/sidebar. Then they can not be duped in individual
fullscreen/minimized/sidebar views.

https://codereview.appspot.com/12935046/diff/1/app/views/utils.js
File app/views/utils.js (right):

https://codereview.appspot.com/12935046/diff/1/app/views/utils.js#newcode1484
app/views/utils.js:1484: _bindViewmodeControls: function(controls) {
you pass in controls, but continue to use this.controls for the events.

https://codereview.appspot.com/12935046/

Revision history for this message
Madison Scott-Clary (makyo) wrote :

LGTM with Rick's suggestions - good code and ideas; I'm good with Rick's
changes, too.

https://codereview.appspot.com/12935046/

Revision history for this message
Jeff Pihach (hatch) wrote :

LGTM with rick's comments about testing

https://codereview.appspot.com/12935046/diff/1/app/subapps/browser/views/view.js
File app/subapps/browser/views/view.js (right):

https://codereview.appspot.com/12935046/diff/1/app/subapps/browser/views/view.js#newcode44
app/subapps/browser/views/view.js:44:
Y.juju.views.utils.viewmodeControllingView
this can be shortened to just 'views' as it's defined above

https://codereview.appspot.com/12935046/diff/1/app/subapps/browser/views/view.js#newcode227
app/subapps/browser/views/view.js:227: _toggleMinimized: function(ev) {
I note that this is called by the extension would be helpful.

https://codereview.appspot.com/12935046/diff/1/app/views/utils.js
File app/views/utils.js (right):

https://codereview.appspot.com/12935046/diff/1/app/views/utils.js#newcode1471
app/views/utils.js:1471: * Constructor
This is technically incorrect, the constructor is the function on 1466

https://codereview.appspot.com/12935046/diff/1/app/views/utils.js#newcode1475
app/views/utils.js:1475: _initViewmodeControllingView: function() {
noop?

https://codereview.appspot.com/12935046/diff/1/app/views/utils.js#newcode1536
app/views/utils.js:1536: * @method _toggle_sidebar
_toggleMinimized

https://codereview.appspot.com/12935046/

975. By j.c.sackett

Move viewmode controls extension to viewmode widget module.

976. By j.c.sackett

Test updates.

977. By j.c.sackett

Merged trunk.

978. By j.c.sackett

Shut up, lint.

Revision history for this message
j.c.sackett (jcsackett) wrote :
Revision history for this message
Richard Harding (rharding) wrote :

LGTM with comment on naming and a check that there's test cases for the
minimized method required by the two views fullscreen/sidebar.

https://codereview.appspot.com/12935046/diff/9001/app/subapps/browser/views/minimized.js
File app/subapps/browser/views/minimized.js (right):

https://codereview.appspot.com/12935046/diff/9001/app/subapps/browser/views/minimized.js#newcode40
app/subapps/browser/views/minimized.js:40:
Y.juju.widgets.ViewmodeControllingViewExtension
The widget is ViewmodeControls, I'd stick to that vs Controlling.

https://codereview.appspot.com/12935046/diff/9001/test/test_viewmode_controls_widget.js
File test/test_viewmode_controls_widget.js (right):

https://codereview.appspot.com/12935046/diff/9001/test/test_viewmode_controls_widget.js#newcode173
test/test_viewmode_controls_widget.js:173: view._toggleMinimized =
function() {
so we need individual tests on this then since it's not implemented in
the extension right?

https://codereview.appspot.com/12935046/

Revision history for this message
j.c.sackett (jcsackett) wrote :

On 2013/08/23 13:08:13, rharding wrote:

https://codereview.appspot.com/12935046/diff/9001/app/subapps/browser/views/minimized.js#newcode40
> app/subapps/browser/views/minimized.js:40:
> Y.juju.widgets.ViewmodeControllingViewExtension
> The widget is ViewmodeControls, I'd stick to that vs Controlling.

Made that switch.

https://codereview.appspot.com/12935046/diff/9001/test/test_viewmode_controls_widget.js#newcode173
> test/test_viewmode_controls_widget.js:173: view._toggleMinimized =
function() {
> so we need individual tests on this then since it's not implemented in
the
> extension right?

You're right; I hadn't intended to *completely* delete the minimzed
tests. Put one back in for the toggle to sidebar. Sidebar already has a
test for toggling to minimized.

https://codereview.appspot.com/12935046/

979. By j.c.sackett

Comments from reviews.

980. By j.c.sackett

Test for minimized toggling to sidebar.

Revision history for this message
j.c.sackett (jcsackett) wrote :

*** Submitted:

Moves viewmode control binding to a mixin

* A new mixin has been created
* Nav functions like _goFullscreen have been moved to the mixin
* * toggleMinimized is just a placeholder, as fullscreen/sidebar need to
do a
different thing than minimized
* The mixin doesn't create controls; it just has them passed in, since
different
views setup the controls differently.

R=rharding, matthew.scott, jeff.pihach
CC=
https://codereview.appspot.com/12935046

https://codereview.appspot.com/12935046/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'app/subapps/browser/views/minimized.js'
2--- app/subapps/browser/views/minimized.js 2013-08-21 13:00:08 +0000
3+++ app/subapps/browser/views/minimized.js 2013-08-23 13:39:30 +0000
4@@ -29,12 +29,6 @@
5 YUI.add('subapp-browser-minimized', function(Y) {
6 var ns = Y.namespace('juju.browser.views'),
7 views = Y.namespace('juju.views');
8-
9- // XXX jcsackett Aug 21 2013 The minimized view currently dupes a bunch of
10- // code from the Mainview. We can't just use mainview here b/c mainview
11- // expects search stuff to exist, which is only true for the sidebar and
12- // fullscreen views. When we add the search bar to always be present, we
13- // should remove this duplication.
14 /**
15 * The minimized state view.
16 *
17@@ -43,7 +37,7 @@
18 *
19 */
20 ns.MinimizedView = Y.Base.create('browser-view-minimized', Y.View, [
21- Y.Event.EventTracker
22+ Y.juju.widgets.ViewmodeControlsViewExtension
23 ], {
24 template: views.Templates.minimized,
25
26@@ -54,71 +48,13 @@
27 },
28
29 /**
30- Binds the viewmode control events to navigation.
31-
32- @method _bindViewmodeControls
33- */
34- _bindViewmodeControls: function() {
35- var container = this.get('container');
36- this.addEvent(
37- this.controls.on(
38- this.controls.EVT_TOGGLE_VIEWABLE, this._toggleViewState, this)
39- );
40-
41- this.addEvent(
42- this.controls.on(
43- this.controls.EVT_FULLSCREEN, this._goFullscreen, this)
44- );
45- this.addEvent(
46- this.controls.on(
47- this.controls.EVT_SIDEBAR, this._goSidebar, this)
48- );
49- },
50-
51- /**
52- Upon clicking the browser icon make sure we re-route to the
53- new form of the UX.
54-
55- @method _goFullscreen
56- @param {Event} ev the click event handler on the button.
57-
58- */
59- _goFullscreen: function(ev) {
60- ev.halt();
61- this.fire('viewNavigate', {
62- change: {
63- viewmode: 'fullscreen'
64- }
65- });
66- },
67-
68- /**
69- Upon clicking the build icon make sure we re-route to the
70- new form of the UX.
71-
72- @method _goSidebar
73- @param {Event} ev the click event handler on the button.
74-
75- */
76- _goSidebar: function(ev) {
77- ev.halt();
78- this.fire('viewNavigate', {
79- change: {
80- viewmode: 'sidebar'
81- }
82- });
83- },
84-
85- /**
86- * Toggle the visibility of the browser. Bound to nav controls in the
87- * view, however this will be expanded to be controlled from the new
88- * constant nav menu outside of the view once it's completed.
89+ * Toggle the visibility of the browser.
90 *
91- * @method _toggle_sidebar
92+ * @method _toggleMinimized
93 * @param {Event} ev event to trigger the toggle.
94 *
95 */
96- _toggleViewState: function(ev) {
97+ _toggleMinimized: function(ev) {
98 ev.halt();
99
100 this.get('container').hide();
101@@ -157,7 +93,7 @@
102 currentViewmode: this.get('oldViewMode')
103 });
104 this.controls.render();
105- this._bindViewmodeControls();
106+ this._bindViewmodeControls(this.controls);
107 }
108
109 }, {
110
111=== modified file 'app/subapps/browser/views/view.js'
112--- app/subapps/browser/views/view.js 2013-08-08 15:17:01 +0000
113+++ app/subapps/browser/views/view.js 2013-08-23 13:39:30 +0000
114@@ -40,7 +40,8 @@
115 *
116 */
117 ns.MainView = Y.Base.create('browser-view-mainview', Y.View, [
118- Y.Event.EventTracker
119+ Y.Event.EventTracker,
120+ Y.juju.widgets.ViewmodeControlsViewExtension
121 ], {
122
123 /**
124@@ -85,20 +86,6 @@
125 */
126 _bindSearchWidgetEvents: function() {
127 var container = this.get('container');
128- this.addEvent(
129- this.controls.on(
130- this.controls.EVT_TOGGLE_VIEWABLE, this._toggleBrowser, this)
131- );
132-
133- this.addEvent(
134- this.controls.on(
135- this.controls.EVT_FULLSCREEN, this._goFullscreen, this)
136- );
137- this.addEvent(
138- this.controls.on(
139- this.controls.EVT_SIDEBAR, this._goSidebar, this)
140- );
141-
142 if (this.search) {
143 this.addEvent(
144 this.search.on(
145@@ -106,7 +93,6 @@
146 );
147 }
148
149-
150 if (this.search) {
151 this.addEvent(
152 this.search.on(
153@@ -137,6 +123,7 @@
154 }
155 }, this);
156 }
157+ this._bindViewmodeControls(this.controls);
158 },
159
160 /**
161@@ -233,11 +220,13 @@
162 view, however this will be expanded to be controlled from the new
163 constant nav menu outside of the view once it's completed.
164
165- @method _toggle_sidebar
166+ This is called by the ViewmodeControlsViewExtension.
167+
168+ @method _toggleMinimized
169 @param {Event} ev event to trigger the toggle.
170
171 */
172- _toggleBrowser: function(ev) {
173+ _toggleMinimized: function(ev) {
174 ev.halt();
175
176 this.fire('viewNavigate', {
177@@ -248,44 +237,9 @@
178 },
179
180 /**
181- Upon clicking the browser icon make sure we re-route to the
182- new form of the UX.
183-
184- @method _goFullscreen
185- @param {Event} ev the click event handler on the button.
186-
187- */
188- _goFullscreen: function(ev) {
189- ev.halt();
190- this.fire('viewNavigate', {
191- change: {
192- viewmode: 'fullscreen'
193- }
194- });
195- },
196-
197- /**
198- Upon clicking the build icon make sure we re-route to the
199- new form of the UX.
200-
201- @method _goSidebar
202- @param {Event} ev the click event handler on the button.
203-
204- */
205- _goSidebar: function(ev) {
206- ev.halt();
207- this.fire('viewNavigate', {
208- change: {
209- viewmode: 'sidebar'
210- }
211- });
212- },
213-
214- /**
215 * Destroy this view and clear from the dom world.
216 *
217 * @method destructor
218- *
219 */
220 destructor: function() {
221 // Clean up any details view we might have hanging around.
222
223=== modified file 'app/views/utils.js'
224--- app/views/utils.js 2013-08-19 20:51:17 +0000
225+++ app/views/utils.js 2013-08-23 13:39:30 +0000
226@@ -1457,10 +1457,11 @@
227 debugger;
228 /*jshint debug:false */
229 });
230+
231 /*
232 * Extension for views to provide an apiFailure method.
233 *
234- * @class apiFailure
235+ * @class apiFailingView
236 */
237 utils.apiFailingView = function() {
238 this._initAPIFailingView();
239
240=== modified file 'app/widgets/viewmode-controls.js'
241--- app/widgets/viewmode-controls.js 2013-07-15 13:17:42 +0000
242+++ app/widgets/viewmode-controls.js 2013-08-23 13:39:30 +0000
243@@ -189,6 +189,78 @@
244 }
245 });
246
247+ /**
248+ * Extension for views to provide viewmode controls.
249+ *
250+ * @class viewmodeControllingView
251+ */
252+ ns.ViewmodeControlsViewExtension = function() {};
253+ ns.ViewmodeControlsViewExtension.prototype = {
254+ /**
255+ * Binds the viewmode controls on the page to the viewmode change events.
256+ *
257+ * @method _bindViewmodeControls
258+ * @param {Y.Widget} controls The viewmode control widget.
259+ */
260+ _bindViewmodeControls: function(controls) {
261+ this._fullscreen = controls.on(
262+ controls.EVT_FULLSCREEN, this._goFullscreen, this);
263+ this._sidebar = controls.on(
264+ controls.EVT_SIDEBAR, this._goSidebar, this);
265+ this._minimized = controls.on(
266+ controls.EVT_TOGGLE_VIEWABLE, this._toggleMinimized, this);
267+ this._destroy = this.on('destroy', function() {
268+ this._fullscreen.detach();
269+ this._sidebar.detach();
270+ this._minimized.detach();
271+ this._destroy.detach();
272+ });
273+ },
274+
275+ /**
276+ Upon clicking the browser icon make sure we re-route to the
277+ new form of the UX.
278+
279+ @method _goFullscreen
280+ @param {Event} ev the click event handler on the button.
281+
282+ */
283+ _goFullscreen: function(ev) {
284+ ev.halt();
285+ this.fire('viewNavigate', {
286+ change: {
287+ viewmode: 'fullscreen'
288+ }
289+ });
290+ },
291+
292+ /**
293+ Upon clicking the build icon make sure we re-route to the
294+ new form of the UX.
295+
296+ @method _goSidebar
297+ @param {Event} ev the click event handler on the button.
298+
299+ */
300+ _goSidebar: function(ev) {
301+ ev.halt();
302+ this.fire('viewNavigate', {
303+ change: {
304+ viewmode: 'sidebar'
305+ }
306+ });
307+ },
308+
309+ /**
310+ * Place holder to toggle the minimized view; in minimized this should show
311+ * sidebar, in sidebar this should show minimized.
312+ * @method _toggleMinimized
313+ * @param {Event} ev event to trigger the toggle.
314+ *
315+ */
316+ _toggleMinimized: function(ev) {}
317+ };
318+
319 }, '0.1.0', {
320 requires: [
321 'base',
322
323=== modified file 'test/test_browser_app.js'
324--- test/test_browser_app.js 2013-08-21 12:58:02 +0000
325+++ test/test_browser_app.js 2013-08-23 13:39:30 +0000
326@@ -33,72 +33,6 @@
327 };
328
329 (function() {
330- describe('browser minimized view', function() {
331- var browser, container, Minimized, view, views, Y;
332-
333- before(function(done) {
334- Y = YUI(GlobalConfig).use(
335- 'juju-views',
336- 'juju-browser',
337- 'juju-tests-utils',
338- 'subapp-browser-minimized', function(Y) {
339- browser = Y.namespace('juju.browser');
340- views = Y.namespace('juju.browser.views');
341- Minimized = views.MinimizedView;
342- done();
343- });
344- });
345-
346- beforeEach(function() {
347- container = Y.namespace('juju-tests.utils').makeContainer('container');
348- addBrowserContainer(Y, container);
349- // Mock out a dummy location for the Store used in view instances.
350- window.juju_config = {
351- charmworldURL: 'http://localhost'
352- };
353-
354- });
355-
356- afterEach(function() {
357- view.destroy();
358- Y.one('#subapp-browser').remove(true);
359- delete window.juju_config;
360- container.remove(true);
361- });
362-
363- it('can route to fullscreen', function(done) {
364- view = new Minimized();
365- view.render();
366- view.on('viewNavigate', function(ev) {
367- assert.equal(ev.change.viewmode, 'fullscreen');
368- done();
369- });
370- view.controls._goFullscreen({halt: function() {} });
371- });
372-
373- it('can route to sidebar via view controls', function(done) {
374- view = new Minimized();
375- view.render();
376- view.on('viewNavigate', function(ev) {
377- assert.equal(ev.change.viewmode, 'sidebar');
378- done();
379- });
380- view.controls._goSidebar({halt: function() {} });
381- });
382-
383- it('can toggle back to oldviewmode via the toggle', function(done) {
384- view = new Minimized({oldViewmode: 'sidebar'});
385- view.render();
386- view.on('viewNavigate', function(ev) {
387- assert.equal(ev.change.viewmode, 'sidebar');
388- done();
389- });
390- view.controls._toggleViewable({halt: function() {} });
391- });
392- });
393- })();
394-
395- (function() {
396 describe('browser fullscreen view', function() {
397 var browser, container, FullScreen, view, views, Y;
398
399@@ -247,6 +181,54 @@
400 })();
401
402 (function() {
403+ describe('browser minimzed view', function() {
404+ var Y, browser, container, view, views, Minimized;
405+
406+ before(function(done) {
407+ Y = YUI(GlobalConfig).use(
408+ 'juju-browser',
409+ 'juju-models',
410+ 'juju-views',
411+ 'juju-tests-utils',
412+ 'subapp-browser-minimized',
413+ function(Y) {
414+ browser = Y.namespace('juju.browser');
415+ views = Y.namespace('juju.browser.views');
416+ Minimized = views.MinimizedView;
417+ done();
418+ });
419+ });
420+
421+ beforeEach(function() {
422+ container = Y.namespace('juju-tests.utils').makeContainer('container');
423+ addBrowserContainer(Y, container);
424+ // Mock out a dummy location for the Store used in view instances.
425+ window.juju_config = {
426+ charmworldURL: 'http://localhost'
427+ };
428+ });
429+
430+ afterEach(function() {
431+ view.destroy();
432+ Y.one('#subapp-browser').remove(true);
433+ delete window.juju_config;
434+ container.remove(true);
435+ });
436+
437+ it('toggles to sidebar', function(done) {
438+ var container = Y.one('#subapp-browser');
439+ view = new Minimized();
440+ view.on('viewNavigate', function(ev) {
441+ assert(ev.change.viewmode === 'sidebar');
442+ done();
443+ });
444+ view.render(container);
445+ view.controls._toggleViewable({halt: function() {}});
446+ });
447+ });
448+ })();
449+
450+ (function() {
451 describe('browser sidebar view', function() {
452 var Y, browser, container, view, views, Sidebar;
453
454
455=== modified file 'test/test_viewmode_controls_widget.js'
456--- test/test_viewmode_controls_widget.js 2013-07-15 13:17:42 +0000
457+++ test/test_viewmode_controls_widget.js 2013-08-23 13:39:30 +0000
458@@ -106,3 +106,75 @@
459 triggered.should.eql(true);
460 });
461 });
462+
463+describe('viewmode control extension', function() {
464+ var Y, container, controls, ViewmodeControls, TestView, view;
465+
466+ before(function(done) {
467+ Y = YUI(GlobalConfig).use(['viewmode-controls',
468+ 'juju-tests-utils',
469+ 'event-simulate',
470+ 'node-event-simulate',
471+ 'node'], function(Y) {
472+ ViewmodeControls = Y.juju.widgets.ViewmodeControls;
473+ TestView = Y.Base.create('testclass', Y.View, [
474+ Y.juju.widgets.ViewmodeControlsViewExtension]);
475+ done();
476+ });
477+ });
478+
479+ beforeEach(function() {
480+ container = Y.namespace('juju-tests.utils').makeContainer('container');
481+ Y.Node.create([
482+ '<div id="content">',
483+ '<div id="browser-nav">',
484+ '<div class="sidebar"></div>',
485+ '<div class="fullscreen"</div>',
486+ '</div>'
487+ ].join('')).appendTo(container);
488+ });
489+
490+ afterEach(function() {
491+ container.remove(true);
492+ if (view) {
493+ view.destroy();
494+ }
495+ if (controls) {
496+ controls.destroy();
497+ }
498+ });
499+
500+ it('can route to fullscreen', function(done) {
501+ view = new TestView();
502+ controls = new ViewmodeControls();
503+ view._bindViewmodeControls(controls);
504+ view.on('viewNavigate', function(ev) {
505+ assert.equal(ev.change.viewmode, 'fullscreen');
506+ done();
507+ });
508+ controls._goFullscreen({halt: function() {} });
509+ });
510+
511+ it('can route to sidebar via view controls', function(done) {
512+ view = new TestView();
513+ controls = new ViewmodeControls();
514+ view._bindViewmodeControls(controls);
515+ view.on('viewNavigate', function(ev) {
516+ assert.equal(ev.change.viewmode, 'sidebar');
517+ done();
518+ });
519+ controls._goSidebar({halt: function() {} });
520+ });
521+
522+ it('can handle toggling the minimized state', function() {
523+ var called = false;
524+ view = new TestView();
525+ controls = new ViewmodeControls();
526+ view._toggleMinimized = function() {
527+ called = true;
528+ };
529+ view._bindViewmodeControls(controls);
530+ controls._toggleViewable({halt: function() {} });
531+ assert.isTrue(called);
532+ });
533+});

Subscribers

People subscribed via source and target branches