Merge lp:~rharding/juju-gui/help-dropdown into lp:juju-gui/experimental

Proposed by Richard Harding
Status: Merged
Merged at revision: 1198
Proposed branch: lp:~rharding/juju-gui/help-dropdown
Merge into: lp:juju-gui/experimental
Diff against target: 1292 lines (+673/-259)
23 files modified
app/app.js (+22/-1)
app/assets/javascripts/view-dropdown-extension.js (+92/-0)
app/index.html (+4/-10)
app/modules-debug.js (+9/-0)
app/subapps/browser/browser.js (+13/-5)
app/templates/help-dropdown.handlebars (+29/-0)
app/templates/notifications.handlebars (+28/-24)
app/templates/onboarding.handlebars (+1/-1)
app/templates/overview.handlebars (+0/-4)
app/views/help-dropdown.js (+113/-0)
app/views/notifications.js (+15/-62)
app/views/onboarding.js (+13/-0)
bin/merge-files (+1/-0)
lib/views/browser/onboarding.less (+5/-0)
lib/views/dropdown.less (+110/-54)
lib/views/stylesheet.less (+6/-69)
test/index.html (+2/-0)
test/test_browser_app.js (+17/-0)
test/test_dropdown_extension.js (+80/-0)
test/test_help_dropdown.js (+97/-0)
test/test_notifications.js (+1/-28)
test/test_onboarding.js (+15/-0)
undocumented (+0/-1)
To merge this branch: bzr merge lp:~rharding/juju-gui/help-dropdown
Reviewer Review Type Date Requested Status
Juju GUI Hackers Pending
Review via email: mp+195116@code.launchpad.net

Description of the change

A dropdown view extension.

This is a refactoring of Huw's branch which created a dropdown widget
 and a "Help & Feedback" dropdown on the header bar.

 original: https://codereview.appspot.com/23410043/

 original original: https://codereview.appspot.com/25390043/

https://codereview.appspot.com/26200043/

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

Reviewers: mp+195116_code.launchpad.net,

Message:
Please take a look.

Description:
A dropdown view extension.

This is a refactoring of Huw's branch which created a dropdown widget
  and a "Help & Feedback" dropdown on the header bar.

  original: https://codereview.appspot.com/23410043/

  original original: https://codereview.appspot.com/25390043/

https://code.launchpad.net/~rharding/juju-gui/help-dropdown/+merge/195116

(do not edit description out of merge proposal)

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

Affected files (+675, -259 lines):
   A [revision details]
   M app/app.js
   A app/assets/javascripts/view-dropdown-extension.js
   M app/index.html
   M app/modules-debug.js
   M app/subapps/browser/browser.js
   A app/templates/help-dropdown.handlebars
   M app/templates/notifications.handlebars
   M app/templates/onboarding.handlebars
   M app/templates/overview.handlebars
   A app/views/help-dropdown.js
   M app/views/notifications.js
   M app/views/onboarding.js
   M bin/merge-files
   M lib/views/browser/onboarding.less
   M lib/views/dropdown.less
   M lib/views/stylesheet.less
   M test/index.html
   M test/test_browser_app.js
   A test/test_dropdown_extension.js
   A test/test_help_dropdown.js
   M test/test_notifications.js
   M test/test_onboarding.js
   M undocumented

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

*** Submitted:

A dropdown view extension.

This is a refactoring of Huw's branch which created a dropdown widget
  and a "Help & Feedback" dropdown on the header bar.

  original: https://codereview.appspot.com/23410043/

  original original: https://codereview.appspot.com/25390043/

R=
CC=
https://codereview.appspot.com/26200043

https://codereview.appspot.com/26200043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'app/app.js'
2--- app/app.js 2013-11-07 21:10:06 +0000
3+++ app/app.js 2013-11-13 18:41:54 +0000
4@@ -126,7 +126,6 @@
5 type: 'juju.views.NotificationsView',
6 preserve: true
7 }
8-
9 },
10
11 /*
12@@ -534,6 +533,7 @@
13 } else {
14 this.dispatch();
15 }
16+ this._renderHelpDropdownView();
17 }, this);
18
19 // Halt the default navigation on the juju logo to allow us to show
20@@ -613,6 +613,23 @@
21 },
22
23 /**
24+ * Handles rendering the help dropdown view on application load.
25+ *
26+ * @method _renderHelpDropdownView
27+ */
28+ _renderHelpDropdownView: function() {
29+ this.helpDropdown = new views.HelpDropdownView({
30+ container: Y.one('#help-dropdown'),
31+ env: this.db.environment
32+ }).render();
33+ // pass in onboarding when we no longer need to support
34+ // fullscreen mode and interact with it directly
35+ this.helpDropdown.on('navigate', function(e) {
36+ this.navigate(e.url);
37+ }, this);
38+ },
39+
40+ /**
41 Calls the deployer import method with the bundle data
42 to deploy the bundle to the environment.
43
44@@ -674,6 +691,9 @@
45 @method destructor
46 */
47 destructor: function() {
48+ if (this.helpDropdown) {
49+ this.helpDropdown.destroy();
50+ }
51 if (this._keybindings) {
52 this._keybindings.detach();
53 }
54@@ -1344,6 +1364,7 @@
55 'juju-ghost-inspector',
56 'juju-view-bundle',
57 'viewmode-controls',
58+ 'help-dropdown',
59 'bundle-import-extension'
60 ]
61 });
62
63=== added file 'app/assets/javascripts/view-dropdown-extension.js'
64--- app/assets/javascripts/view-dropdown-extension.js 1970-01-01 00:00:00 +0000
65+++ app/assets/javascripts/view-dropdown-extension.js 2013-11-13 18:41:54 +0000
66@@ -0,0 +1,92 @@
67+/*
68+This file is part of the Juju GUI, which lets users view and manage Juju
69+environments within a graphical interface (https://launchpad.net/juju-gui).
70+Copyright (C) 2012-2013 Canonical Ltd.
71+
72+This program is free software: you can redistribute it and/or modify it under
73+the terms of the GNU Affero General Public License version 3, as published by
74+the Free Software Foundation.
75+
76+This program is distributed in the hope that it will be useful, but WITHOUT
77+ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
78+SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
79+General Public License for more details.
80+
81+You should have received a copy of the GNU Affero General Public License along
82+with this program. If not, see <http://www.gnu.org/licenses/>.
83+*/
84+
85+'use strict';
86+
87+
88+YUI.add('view-dropdown-extension', function(Y) {
89+
90+ /**
91+ * Extension for a Y.View derrived class. Adds the dropdown
92+ * functionality to the view. You need to call the _addDropdownFunc
93+ * from the render method of the view to trigger because
94+ * Y.View's do not offer any render event.
95+ *
96+ * @namespace juju
97+ * @class Dropdown
98+ */
99+ function Dropdown() {}
100+
101+ Dropdown.prototype = {
102+ /**
103+ * Toggle the visibility of the dropdown panel.
104+ *
105+ * @method _toggleDropdown
106+ * @param {Event} ev the click event from the control.
107+ */
108+ __toggleDropdown: function(ev) {
109+ ev.halt();
110+ this.get('container').toggleClass('open');
111+ },
112+
113+ /**
114+ * Hide the dropdown panel.
115+ *
116+ * @method close
117+ */
118+ __close: function() {
119+ this.get('container').removeClass('open');
120+ },
121+
122+ /**
123+ * Sets up events and binds them to listeners.
124+ *
125+ * @method __bindUI
126+ * @param {Y.Node} container The views container element.
127+ */
128+ __bindUI: function(container) {
129+ this.addEvent(
130+ container.one('.menu-link').on(
131+ 'click', this.__toggleDropdown, this));
132+ this.addEvent(
133+ container.on(
134+ 'clickoutside', this.__close, this));
135+ },
136+
137+ /**
138+ * Sets up the DOM nodes and renders them to the DOM.
139+ *
140+ * @method _addDropdownFunc
141+ */
142+ _addDropdownFunc: function() {
143+ var container = this.get('container');
144+ if (container) {
145+ container.addClass('dropdown-menu');
146+ this.__bindUI(container);
147+ }
148+ }
149+ };
150+
151+ Y.namespace('juju').Dropdown = Dropdown;
152+
153+}, '0.1.0', {
154+ requires: [
155+ 'event-tracker',
156+ 'view'
157+ ]
158+});
159
160=== modified file 'app/index.html'
161--- app/index.html 2013-11-07 16:50:47 +0000
162+++ app/index.html 2013-11-13 18:41:54 +0000
163@@ -147,19 +147,13 @@
164 </a>
165 </li>
166 <li class="notifications-nav">
167- <div id="notifications"></div>
168- <div class="notifier-box"></div>
169+ <span id="notifications"></span>
170+ <span class="notifier-box"></span>
171 </li>
172 </ul>
173 <ul class="right-nav">
174- <li class="help-dropdown">
175- <a href="">
176- Help & Feedback
177- <span class="expand">
178- <i class="sprite chevron_down more"></i>
179- <i class="sprite chevron_up less hidden"></i>
180- </span>
181- </a>
182+ <li>
183+ <span id="help-dropdown"></span>
184 </li>
185 <li>
186 <a href="" id="logout-trigger" class="button inverse">Logout</a>
187
188=== modified file 'app/modules-debug.js'
189--- app/modules-debug.js 2013-11-07 15:49:57 +0000
190+++ app/modules-debug.js 2013-11-13 18:41:54 +0000
191@@ -185,6 +185,10 @@
192 fullpath: '/juju-ui/assets/javascripts/bundle-import-extension.js'
193 },
194
195+ 'view-dropdown-extension': {
196+ fullpath: '/juju-ui/assets/javascripts/view-dropdown-extension.js'
197+ },
198+
199 'sub-app': {
200 fullpath: '/juju-ui/assets/javascripts/sub-app.js'
201 },
202@@ -233,6 +237,10 @@
203 fullpath: '/juju-ui/views/notifications.js'
204 },
205
206+ 'juju-help-dropdown': {
207+ fullpath: '/juju-ui/views/help-dropdown.js'
208+ },
209+
210 'juju-view-environment': {
211 fullpath: '/juju-ui/views/environment.js'
212 },
213@@ -267,6 +275,7 @@
214 'd3-components',
215 'juju-templates',
216 'juju-notifications',
217+ 'juju-help-dropdown',
218 'juju-view-utils',
219 'juju-topology',
220 'juju-view-environment',
221
222=== modified file 'app/subapps/browser/browser.js'
223--- app/subapps/browser/browser.js 2013-11-05 18:10:05 +0000
224+++ app/subapps/browser/browser.js 2013-11-13 18:41:54 +0000
225@@ -626,13 +626,18 @@
226 * Create a 'welcome' message walkthrough for new users.
227 *
228 * @method renderOnboarding
229+ * @param {Boolean} force Whether it should force render the onboarding.
230 */
231- renderOnboarding: function() {
232+ renderOnboarding: function(force) {
233 // Need to check onboarding exists due to the double dispatch bug.
234 this._onboarding = new Y.juju.views.OnboardingView({
235 'container': '#onboarding'
236 });
237
238+ if (force) {
239+ this._onboarding.reset();
240+ }
241+
242 if (!this._onboarding.get('seen')) {
243 this._onboarding.render();
244 }
245@@ -861,10 +866,13 @@
246 // Only show the onboarding messaging if we're hitting the sidebar view
247 // without any extra url bits to the user. It's meant for a fresh user
248 // to see, not someone doing what they know they want to do.
249- if (!this._onboarding) {
250- if (!this._viewState.search &&
251- !this._viewState.charmID) {
252- this.renderOnboarding();
253+ var force = localStorage.getItem('force-onboarding');
254+ // Reset force-onboarding so that the next request to /sidebar acts normal
255+ localStorage.setItem('force-onboarding', '');
256+
257+ if (!this._onboarding || force) {
258+ if (!this._viewState.search && !this._viewState.charmID) {
259+ this.renderOnboarding(force);
260 }
261 }
262
263
264=== added file 'app/templates/help-dropdown.handlebars'
265--- app/templates/help-dropdown.handlebars 1970-01-01 00:00:00 +0000
266+++ app/templates/help-dropdown.handlebars 2013-11-13 18:41:54 +0000
267@@ -0,0 +1,29 @@
268+<a href="" class="menu-link">
269+ Help & feedback
270+ <span class="expand">
271+ <i class="sprite chevron_down more"></i>
272+ <i class="sprite chevron_up less"></i>
273+ </span>
274+</a>
275+<span class="dropdown right narrow">
276+ <ul>
277+ <li>
278+ <a href="" class="start-onboarding">Tutorial</a>
279+ </li>
280+ <li>
281+ <a href="http://juju.ubuntu.com/"
282+ target="_blank">Visit: juju.ubuntu.com</a>
283+ </li>
284+ <li>
285+ <a href="http://juju.ubuntu.com/docs"
286+ target="_blank">Read the documentation</a>
287+ </li>
288+ <li>
289+ <a href="https://www.surveymonkey.com/s/jujugui"
290+ target="_blank">Take the survey</a>
291+ </li>
292+ <li class="landscape-url hidden">
293+ <a href="">Landscape</a>
294+ </li>
295+ </ul>
296+</span>
297
298=== modified file 'app/templates/notifications.handlebars'
299--- app/templates/notifications.handlebars 2013-10-24 18:02:16 +0000
300+++ app/templates/notifications.handlebars 2013-11-13 18:41:54 +0000
301@@ -1,25 +1,29 @@
302-<span class="{{open}}">
303- <a href="" id="notify-indicator" data-target="notify-list">
304- {{count}}
305- </a>
306- <span id="notify-list" class="dropdown">
307- <h3>
308- {{count}} notifications
309- </h3>
310- <ul>
311- {{#notifications}}
312- <li id="{{clientId}}" class="notice">
313- <div>
314- <h4>{{title}}</h4>
315- <div>
316- {{message}}
317- </div>
318- <small data-timestamp="{{timestamp}}"
319- class="timestamp">{{humanizeTime timestamp}}</small>
320- </div>
321- </li>
322- {{/notifications}}
323- </ul>
324- </span>
325- Notifications
326+<a href="" class="menu-link">
327+ <span id="notify-indicator">{{ count }}</span>
328+ Notifications
329+ <span class="expand">
330+ <i class="sprite chevron_down more"></i>
331+ <i class="sprite chevron_up less"></i>
332+ </span>
333+</a>
334+<span class="dropdown">
335+ <header>
336+ {{count}} notifications
337+ </header>
338+ {{#if notifications}}
339+ <ul>
340+ {{#notifications}}
341+ <li id="{{clientId}}" class="notice">
342+ <h4>{{title}}</h4>
343+ {{message}}
344+ <small data-timestamp="{{timestamp}}">
345+ {{humanizeTime timestamp}}
346+ </small>
347+ </li>
348+ {{/notifications}}
349+ </ul>
350+ {{else}}
351+ <p>No notifications</p>
352+ {{/if}}
353+ <footer></footer>
354 </span>
355
356=== modified file 'app/templates/onboarding.handlebars'
357--- app/templates/onboarding.handlebars 2013-10-23 18:09:52 +0000
358+++ app/templates/onboarding.handlebars 2013-11-13 18:41:54 +0000
359@@ -5,7 +5,7 @@
360 </div>
361 <div id="onboarding-screen-mask"></div>
362 <div id="onboarding-message">
363- <a class="onboarding-cross sprite close-inspector-normal" href="" ></a>
364+ <button class="onboarding-cross sprite close-inspector-normal" href="" ></button>
365 <div class="panel panel-0">
366 <h3 class="header type3">Welcome to Juju</h3>
367 <p class="text type11">Use Juju to quickly create, configure and deploy your application architectures to public clouds like EC2, Azure and HP, any OpenStack cloud and even your own Ubuntu based laptop with Charms.</p>
368
369=== modified file 'app/templates/overview.handlebars'
370--- app/templates/overview.handlebars 2013-10-18 16:47:34 +0000
371+++ app/templates/overview.handlebars 2013-11-13 18:41:54 +0000
372@@ -22,10 +22,6 @@
373 <div class="menu-title">Select relation type:</div>
374 <ul/>
375 </div>
376- <div id="feedback-box">
377- <a target="_blank"
378- href="https://www.surveymonkey.com/s/jujugui">Feedback</a>
379- </div>
380 {{> right-sidebar }}
381 </div>
382 <div id="rmrelation-modal-panel"></div>
383
384=== added file 'app/views/help-dropdown.js'
385--- app/views/help-dropdown.js 1970-01-01 00:00:00 +0000
386+++ app/views/help-dropdown.js 2013-11-13 18:41:54 +0000
387@@ -0,0 +1,113 @@
388+/*
389+This file is part of the Juju GUI, which lets users view and manage Juju
390+environments within a graphical interface (https://launchpad.net/juju-gui).
391+Copyright (C) 2012-2013 Canonical Ltd.
392+
393+This program is free software: you can redistribute it and/or modify it under
394+the terms of the GNU Affero General Public License version 3, as published by
395+the Free Software Foundation.
396+
397+This program is distributed in the hope that it will be useful, but WITHOUT
398+ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
399+SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
400+General Public License for more details.
401+
402+You should have received a copy of the GNU Affero General Public License along
403+with this program. If not, see <http://www.gnu.org/licenses/>.
404+*/
405+
406+'use strict';
407+
408+/**
409+ * Provide the help dropdown view.
410+ *
411+ * @module views
412+ */
413+
414+YUI.add('help-dropdown', function(Y) {
415+
416+ var views = Y.namespace('juju.views'),
417+ widgets = Y.namespace('juju.widgets'),
418+ Templates = views.Templates;
419+
420+ /**
421+ * The view associated with the help dropdown.
422+ *
423+ * @class HelpDropdownView
424+ */
425+ var HelpDropdownView = Y.Base.create('HelpDropdownView', Y.View,
426+ [
427+ Y.juju.Dropdown,
428+ Y.Event.EventTracker
429+ ], {
430+ template: Templates['help-dropdown'],
431+
432+ events: {
433+ '.start-onboarding': {
434+ click: '_startOnboarding'
435+ }
436+ },
437+
438+ /**
439+ * Start the onboarding tutorial.
440+ *
441+ * @method _startOnboarding
442+ * @param {Event} ev the click event from the control.
443+ * @private
444+ */
445+ _startOnboarding: function(ev) {
446+ ev.halt();
447+ // Added by the view-dropdown-extension.js
448+ this.__close(); // Closes itself
449+ // Because we need the app to be in sidebar mode when
450+ // the user views the onboarding we navigate to it.
451+ // Once fullscreen is removed we can interact with
452+ // onboarding here instead of in browser.js
453+ localStorage.setItem('force-onboarding', true);
454+ this.fire('navigate', { url: '/sidebar' });
455+ },
456+
457+ /**
458+ * Show the Landscape URL if available.
459+ *
460+ * @method _displayLandscapeURL
461+ * @private
462+ */
463+ _displayLandscapeURL: function() {
464+ var env = this.get('env'),
465+ url = this.get('container').one('.landscape-url'),
466+ baseLandscapeURL = views.utils.getLandscapeURL(env);
467+
468+ if (baseLandscapeURL) {
469+ url.one('a').setAttribute('href', baseLandscapeURL);
470+ url.removeClass('hidden');
471+ }
472+ },
473+
474+ /**
475+ * Sets up the DOM nodes and renders them to the DOM.
476+ *
477+ * @method render
478+ */
479+ render: function() {
480+ var container = this.get('container');
481+ container.setHTML(this.template());
482+ this._displayLandscapeURL();
483+ // Added by the view-dropdown-extension.js
484+ this._addDropdownFunc();
485+ return this;
486+ }
487+ });
488+
489+ views.HelpDropdownView = HelpDropdownView;
490+
491+}, '0.1.0', {
492+ requires: [
493+ 'view',
494+ 'juju-view-utils',
495+ 'event-tracker',
496+ 'node',
497+ 'handlebars',
498+ 'view-dropdown-extension'
499+ ]
500+});
501
502=== modified file 'app/views/notifications.js'
503--- app/views/notifications.js 2013-11-07 21:10:06 +0000
504+++ app/views/notifications.js 2013-11-13 18:41:54 +0000
505@@ -90,38 +90,6 @@
506 },
507
508 /**
509- * Event handler for clicking the notification icon.
510- *
511- * @method notifyToggle
512- */
513- notifyToggle: function(evt) {
514- evt.halt();
515- var container = this.get('container'),
516- notifications = this.get('notifications'),
517- target = evt.target.getAttribute('data-target'),
518- el = container.one('#' + target),
519- parent = el.ancestor();
520-
521- if (notifications.size() === 0) {
522- return;
523- }
524-
525- if (parent && parent.hasClass('open')) {
526- el.hide(true);
527- }
528- else {
529- el.show(true);
530- }
531-
532- if (parent) {
533- parent.toggleClass('open');
534- }
535-
536- el.toggleClass('active');
537-
538- },
539-
540- /**
541 * Select/click on a notice. Currently this just removes it from the
542 * model_list.
543 *
544@@ -230,7 +198,11 @@
545 * @class NotificationsView
546 */
547 var NotificationsView = Y.Base.create('NotificationsView',
548- NotificationsBaseView, [], {
549+ NotificationsBaseView,
550+ [
551+ Y.Event.EventTracker,
552+ Y.juju.Dropdown
553+ ], {
554 template: Templates.notifications,
555
556 /*
557@@ -244,12 +216,6 @@
558 seen: false
559 },
560
561- events: {
562- '#notify-indicator': {
563- click: 'notifyToggle'
564- }
565- },
566-
567 /**
568 * @method getShowable
569 */
570@@ -262,30 +228,15 @@
571 });
572 },
573
574- close: function() {
575- var container = this.get('container');
576- if (!container) {
577- return;
578- }
579-
580- var indicator = container.one('#notify-indicator'),
581- list = container.one('#notify-list');
582-
583- if (!indicator) {
584- return;
585- }
586- var parent = indicator.ancestor();
587-
588- if (parent && parent.hasClass('open')) {
589- indicator.ancestor().removeClass('open');
590- list.hide();
591- indicator.removeClass('active');
592- }
593- },
594-
595+ /**
596+ Renders the notification view and the dropdown widget.
597+
598+ @method render
599+ */
600 render: function() {
601 NotificationsView.superclass.render.apply(this, arguments);
602- this.get('container').on('clickoutside', this.close, this);
603+ // Added by the view-dropdown-extension.js
604+ this._addDropdownFunc();
605 return this;
606 }
607
608@@ -298,6 +249,8 @@
609 'juju-view-utils',
610 'node',
611 'handlebars',
612- 'notifier'
613+ 'notifier',
614+ 'view-dropdown-extension',
615+ 'event-tracker'
616 ]
617 });
618
619=== modified file 'app/views/onboarding.js'
620--- app/views/onboarding.js 2013-10-21 23:05:08 +0000
621+++ app/views/onboarding.js 2013-11-13 18:41:54 +0000
622@@ -72,6 +72,7 @@
623 var container = this.get('container');
624 container.hide();
625 Y.one('#environment-help').removeClass('hidden');
626+ localStorage.setItem('force-onboarding', '');
627 },
628
629 /**
630@@ -182,6 +183,18 @@
631 },
632
633 /**
634+ Sets the onboarding index back to 0, and the localstorage onboarding
635+ flag to undefined.
636+
637+ @method reset
638+ */
639+ reset: function() {
640+ this.onboardingIndex = 0;
641+ localStorage.setItem('onboarding', '');
642+ this.get('container').empty();
643+ },
644+
645+ /**
646 * Render the page.
647 *
648 * Reveal the mask element, and show the onboarding window.
649
650=== modified file 'bin/merge-files'
651--- bin/merge-files 2013-11-07 15:49:57 +0000
652+++ bin/merge-files 2013-11-13 18:41:54 +0000
653@@ -89,6 +89,7 @@
654 'app/assets/javascripts/app-subapp-extension.js',
655 'app/assets/javascripts/app-cookies-extension.js',
656 'app/assets/javascripts/bundle-import-extension.js',
657+ 'app/assets/javascripts/view-dropdown-extension.js',
658 'app/assets/javascripts/d3-components.js',
659 'app/assets/javascripts/d3.min.js',
660 'app/assets/javascripts/d3.status.js',
661
662=== modified file 'lib/views/browser/onboarding.less'
663--- lib/views/browser/onboarding.less 2013-10-29 02:29:58 +0000
664+++ lib/views/browser/onboarding.less 2013-11-13 18:41:54 +0000
665@@ -58,6 +58,11 @@
666 color: @inspector-text-color;
667 }
668
669+ button {
670+ border: none;
671+ padding: 0;
672+ }
673+
674 img {
675 position: absolute;
676 }
677
678=== modified file 'lib/views/dropdown.less'
679--- lib/views/dropdown.less 2013-10-29 03:07:12 +0000
680+++ lib/views/dropdown.less 2013-11-13 18:41:54 +0000
681@@ -1,62 +1,118 @@
682-.dropdown {
683- .customize-scrollbar;
684- display: none;
685- position: absolute;
686- z-index: 1000;
687- top: @navbar-height;
688- left: 0;
689- width: 290px;
690- background-color: #eee;
691- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
692-
693- &.active {
694+.dropdown-menu {
695+ position: relative;
696+ display: block;
697+ margin: -20px -20px 0 -20px;
698+
699+ &.open {
700+ .menu-link {
701+ background-color: #000;
702+
703+ .expand {
704+ .more {
705+ display: none;
706+ }
707+ .less {
708+ display: inline-block;
709+ }
710+ }
711+ }
712+ .dropdown {
713+ display: block;
714+ }
715+ }
716+ .menu-link {
717+ .border-box;
718 display: block;
719- }
720- a,
721- li {
722- color: #333;
723- }
724- h3 {
725- padding: 11px 20px;
726- background-color: #000;
727- color: #f2f2e6;
728- font-size: 16px;
729- font-weight: 300;
730- }
731- ul {
732- max-height: 500px;
733- overflow-y: auto;
734-
735+ height: @navbar-height;
736+ padding: 20px 20px 0 20px;
737+ color: #d7d3d0;
738+
739+ .expand {
740+ margin-left: 5px;
741+
742+ .less {
743+ display: none;
744+ }
745+ }
746+ }
747+ .dropdown {
748+ .customize-scrollbar;
749+ display: none;
750+ position: absolute;
751+ z-index: 1000;
752+ top: @navbar-height;
753+ left: 0;
754+ width: 290px;
755+ background-color: #eee;
756+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
757+
758+ &.right {
759+ left: auto;
760+ right: 0;
761+ text-align: right;
762+ }
763+ &.narrow {
764+ width: 230px;
765+ }
766+ a,
767+ p,
768 li {
769- border-top: 1px solid #efefef;
770- border-bottom: 1px solid #e2e2e2;
771+ color: @text-colour;
772+ }
773+ p {
774+ padding: 11px 20px;
775+ }
776+ header,
777+ footer {
778+ background-color: #000;
779+ }
780+ header {
781+ padding: 11px 20px;
782+ color: #f2f2e6;
783+ font-size: 16px;
784+ font-weight: 300;
785+ }
786+ footer {
787 padding: 20px;
788-
789- &:first-child {
790- border-top: none;
791- }
792- &:last-child {
793- border-bottom: none;
794- }
795+ border-radius: 0 0 @border-radius @border-radius;
796 }
797- // Notification items
798- .notice {
799- h4 {
800- font-weight: normal;
801- margin-bottom: 10px;
802- }
803- small {
804- display: block;
805- margin-top: 5px;
806- font-size: 10px;
807- color: #d0d0d0;
808- }
809- .error {
810- background-color: rgba(255, 0, 0, 0.1);
811+ ul {
812+ max-height: 500px;
813+ overflow-y: auto;
814+
815+ li {
816+ border-top: 1px solid #efefef;
817+ border-bottom: 1px solid #e2e2e2;
818+
819+ &:first-child {
820+ border-top: none;
821+ }
822+ &:last-child {
823+ border-bottom: none;
824+ }
825+ a {
826+ display: block;
827+ padding: 15px 20px;
828+
829+ &:hover {
830+ background-color: #e2e2e2;
831+ }
832+ }
833+ }
834+ // Notification items
835+ .notice {
836+ padding: 20px;
837+
838+ h4 {
839+ margin-bottom: 10px;
840+ }
841+ small {
842+ display: block;
843+ margin-top: 5px;
844+ font-size: 11px;
845+ color: #aaa;
846+ }
847 }
848 }
849 }
850 }
851-.open .dropdown {
852- display: block;
853-}
854
855=== modified file 'lib/views/stylesheet.less'
856--- lib/views/stylesheet.less 2013-11-07 15:49:57 +0000
857+++ lib/views/stylesheet.less 2013-11-13 18:41:54 +0000
858@@ -175,16 +175,13 @@
859 */
860 .navbar {
861 min-width: 800px;
862- // Need to subtract the bottom-border-height.
863 height: @navbar-height;
864 margin: -@navbar-height 0 0 0;
865 background-color: #221e1b;
866
867 &,
868- a {
869+ & > ul > li > a {
870 color: #d7d3d0;
871- font-size: 14px;
872- font-weight: 300;
873 }
874
875 ul {
876@@ -277,24 +274,11 @@
877 }
878 &.notifications-nav {
879 position: relative;
880- padding: 0;
881-
882- #notifications {
883- & > span {
884- .border-box;
885- display: block;
886- height: @navbar-height;
887- padding: 15px 20px 0 20px;
888-
889- &.open {
890- background-color: #000;
891- }
892- #notify-indicator {
893- .button;
894- margin: 0 9px 0 0;
895- padding: 6px 12px;
896- }
897- }
898+
899+ #notify-indicator {
900+ .button;
901+ margin: -5px 9px 0 0;
902+ padding: 6px 12px;
903 }
904 }
905 }
906@@ -314,18 +298,6 @@
907 background-color: #1f1b18;
908 }
909 }
910- .help-dropdown {
911- display: none;
912- position: relative;
913-
914- .expand {
915- margin-left: 5px;
916-
917- .hidden {
918- display: none;
919- }
920- }
921- }
922 }
923 }
924 #content {
925@@ -717,41 +689,6 @@
926 }
927
928 /*
929- * Feedback box
930- * Appears as a tab on the side of the canvas.
931- */
932-#feedback-box {
933- position: absolute;
934- right: -40px;
935- bottom: 100px;
936- /* Up the z-index so the feedback box will overlay the zoom slider as it
937- * expands when hovered over */
938- z-index: 10;
939-
940- a {
941- color: #fff;
942- background-color: @charm-panel-orange;
943- display: block;
944- line-height: 30px;
945- height: 40px;
946- font-weight: 500;
947- font-size: 16px;
948- padding: 0 10px;
949- margin-right: 3px;
950- -webkit-transition: all 0.15s ease-in-out;
951- -moz-transition: all 0.15s ease-in-out;
952- transition: all 0.15s ease-in-out;
953- .rotate(-90deg);
954-
955- &:hover {
956- text-decoration: none;
957- background-color: #df6920;
958- margin-right: 13px;
959- }
960- }
961-}
962-
963-/*
964 * Canvas zoom control
965 */
966 .zoom-controls {
967
968=== modified file 'test/index.html'
969--- test/index.html 2013-10-31 14:16:29 +0000
970+++ test/index.html 2013-11-13 18:41:54 +0000
971@@ -70,6 +70,7 @@
972 <script src="test_databinding.js"></script>
973 <script src="test_d3_components.js"></script>
974 <script src="test_d3_status.js"></script>
975+ <script src="test_dropdown_extension.js"></script>
976 <script src="test_endpoints.js"></script>
977 <script src="test_env.js"></script>
978 <script src="test_env_go.js"></script>
979@@ -79,6 +80,7 @@
980 <script src="test_fakebackend.js"></script>
981 <script src="test_filter_widget.js"></script>
982 <script src="test_ghost_inspector.js"></script>
983+ <script src="test_help_dropdown.js"></script>
984 <script src="test_inspector_charm.js"></script>
985 <script src="test_inspector_constraints.js"></script>
986 <script src="test_inspector_overview.js"></script>
987
988=== modified file 'test/test_browser_app.js'
989--- test/test_browser_app.js 2013-11-05 18:10:05 +0000
990+++ test/test_browser_app.js 2013-11-13 18:41:54 +0000
991@@ -1324,6 +1324,23 @@
992 assert.deepEqual(hits, expected);
993 });
994
995+ it('onboarding is rendered with force-onboarding query', function(done) {
996+ var req = {
997+ path: '/sidebar'
998+ };
999+
1000+ localStorage.setItem('force-onboarding', true);
1001+
1002+ browser.renderOnboarding = function(force) {
1003+ assert.equal(force, 'true');
1004+ assert.equal(localStorage.getItem('force-onboarding'), '');
1005+ assert.deepEqual(hits.sidebar, true);
1006+ done();
1007+ };
1008+
1009+ browser.routeView(req, undefined, function() {});
1010+ });
1011+
1012 it('/minimized dispatches correctly', function() {
1013 var req = {
1014 path: '/minimized',
1015
1016=== added file 'test/test_dropdown_extension.js'
1017--- test/test_dropdown_extension.js 1970-01-01 00:00:00 +0000
1018+++ test/test_dropdown_extension.js 2013-11-13 18:41:54 +0000
1019@@ -0,0 +1,80 @@
1020+/*
1021+This file is part of the Juju GUI, which lets users view and manage Juju
1022+environments within a graphical interface (https://launchpad.net/juju-gui).
1023+Copyright (C) 2012-2013 Canonical Ltd.
1024+
1025+This program is free software: you can redistribute it and/or modify it under
1026+the terms of the GNU Affero General Public License version 3, as published by
1027+the Free Software Foundation.
1028+
1029+This program is distributed in the hope that it will be useful, but WITHOUT
1030+ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1031+SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
1032+General Public License for more details.
1033+
1034+You should have received a copy of the GNU Affero General Public License along
1035+with this program. If not, see <http://www.gnu.org/licenses/>.
1036+*/
1037+
1038+'use strict';
1039+
1040+
1041+describe('dropdown view extension', function() {
1042+ var Y, container, view, View;
1043+
1044+ before(function(done) {
1045+ Y = YUI(GlobalConfig).use(['view-dropdown-extension',
1046+ 'juju-tests-utils',
1047+ 'event-simulate',
1048+ 'node-event-simulate',
1049+ 'node'], function(Y) {
1050+
1051+ View = Y.Base.create('dropdown', Y.View, [
1052+ Y.juju.Dropdown,
1053+ Y.Event.EventTracker
1054+ ], {
1055+ template: '<a href="" class="menu-link"></a>' +
1056+ '<div class="dropdown"></div>',
1057+
1058+ render: function() {
1059+ this.get('container').setHTML(this.template);
1060+ this._addDropdownFunc();
1061+ return this;
1062+ }
1063+ });
1064+ done();
1065+ });
1066+ });
1067+
1068+ beforeEach(function() {
1069+ container = Y.namespace('juju-tests.utils').makeContainer();
1070+ view = new View({ container: container }).render();
1071+ });
1072+
1073+ afterEach(function() {
1074+ view.destroy();
1075+ container.remove().destroy(true);
1076+ });
1077+
1078+ it('should add the dropdown-menu class to the views container', function() {
1079+ assert.isTrue(container.hasClass('dropdown-menu'));
1080+ });
1081+
1082+ it('should open when the primary link is clicked', function(done) {
1083+ var link = container.one('.menu-link');
1084+ link.after('click', function() {
1085+ assert.isTrue(container.hasClass('open'));
1086+ done();
1087+ });
1088+ link.simulate('click');
1089+ });
1090+
1091+ it('should close when clicking outside the view', function() {
1092+ container.one('.menu-link').simulate('click');
1093+ assert.isTrue(container.hasClass('open'));
1094+
1095+ Y.one('body').simulate('click');
1096+ assert.isFalse(container.hasClass('open'));
1097+ });
1098+
1099+});
1100
1101=== added file 'test/test_help_dropdown.js'
1102--- test/test_help_dropdown.js 1970-01-01 00:00:00 +0000
1103+++ test/test_help_dropdown.js 2013-11-13 18:41:54 +0000
1104@@ -0,0 +1,97 @@
1105+/*
1106+This file is part of the Juju GUI, which lets users view and manage Juju
1107+environments within a graphical interface (https://launchpad.net/juju-gui).
1108+Copyright (C) 2012-2013 Canonical Ltd.
1109+
1110+This program is free software: you can redistribute it and/or modify it under
1111+the terms of the GNU Affero General Public License version 3, as published by
1112+the Free Software Foundation.
1113+
1114+This program is distributed in the hope that it will be useful, but WITHOUT
1115+ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1116+SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
1117+General Public License for more details.
1118+
1119+You should have received a copy of the GNU Affero General Public License along
1120+with this program. If not, see <http://www.gnu.org/licenses/>.
1121+*/
1122+
1123+'use strict';
1124+
1125+describe('help dropdown view', function() {
1126+
1127+ var db, envAnno, helpView, views, landscape, models, viewNode, Y;
1128+
1129+ before(function(done) {
1130+ Y = YUI(GlobalConfig).use(['node',
1131+ 'juju-landscape',
1132+ 'juju-models',
1133+ 'juju-views',
1134+ 'help-dropdown',
1135+ 'juju-tests-utils'], function(Y) {
1136+
1137+ views = Y.namespace('juju.views');
1138+ models = Y.namespace('juju.models');
1139+
1140+ db = new models.Database();
1141+ landscape = new views.Landscape();
1142+ landscape.set('db', db);
1143+
1144+ done();
1145+ });
1146+ });
1147+
1148+ beforeEach(function() {
1149+ envAnno = db.environment.get('annotations');
1150+ viewNode = Y.namespace('juju-tests.utils').makeContainer('help-dropdown');
1151+ });
1152+
1153+ afterEach(function() {
1154+ viewNode.remove().destroy(true);
1155+ if (helpView) {
1156+ helpView.destroy();
1157+ }
1158+ });
1159+
1160+ it('renders the basic list', function() {
1161+ helpView = new views.HelpDropdownView({
1162+ container: viewNode,
1163+ env: db.environment
1164+ }).render();
1165+ // Landscape url should be hidden
1166+ var container = helpView.get('container');
1167+ assert.equal(
1168+ container.one('.landscape-url').getStyle('display'), 'none');
1169+ assert.equal(container.all('li').size(), 5);
1170+ });
1171+
1172+ it('should display the Landscape menu item', function() {
1173+ envAnno['landscape-url'] = 'http://landscape.example.com';
1174+ envAnno['landscape-computers'] = '/computers/criteria/environment:test';
1175+ new views.HelpDropdownView({
1176+ container: viewNode,
1177+ env: db.environment
1178+ }).render();
1179+
1180+ assert.equal(
1181+ viewNode.one('.landscape-url').getStyle('display'), 'list-item');
1182+ assert.equal(
1183+ viewNode.one('.landscape-url a').get('href'),
1184+ 'http://landscape.example.com/computers/criteria/environment:test/');
1185+ });
1186+
1187+ it('can start the onboarding visualization', function(done) {
1188+ helpView = new views.HelpDropdownView({
1189+ container: viewNode,
1190+ env: db.environment
1191+ });
1192+ helpView.on('navigate', function(e) {
1193+ assert.equal(e.url, '/sidebar');
1194+ assert.equal(localStorage.getItem('force-onboarding'), 'true');
1195+ done();
1196+ });
1197+ helpView.render();
1198+ var ob = helpView.get('container').one('.start-onboarding');
1199+ ob.simulate('click');
1200+ });
1201+});
1202
1203=== modified file 'test/test_notifications.js'
1204--- test/test_notifications.js 2013-10-29 23:10:25 +0000
1205+++ test/test_notifications.js 2013-11-13 18:41:54 +0000
1206@@ -159,7 +159,7 @@
1207 nsRouter: nsRouter});
1208 view.render();
1209 // Verify the expected elements appear in the view
1210- container.one('#notify-list').should.not.equal(undefined);
1211+ container.one('.dropdown').should.not.equal(undefined);
1212 container.destroy();
1213 });
1214
1215@@ -330,33 +330,6 @@
1216 notice.get('message').should.equal('');
1217 });
1218
1219-
1220- it('should open on click and close on clickoutside', function(done) {
1221- var container = Y.Node.create(
1222- '<div id="test-container" style="display: none" class="container"/>'),
1223- notifications = new models.NotificationList(),
1224- env = juju.newEnvironment(),
1225- view = new views.NotificationsView({
1226- container: container,
1227- notifications: notifications,
1228- env: env,
1229- nsRouter: nsRouter}).render(),
1230- indicator;
1231-
1232- Y.one('body').append(container);
1233- notifications.add({title: 'testing', 'level': 'error'});
1234- indicator = container.one('#notify-indicator');
1235-
1236- indicator.simulate('click');
1237- indicator.ancestor().hasClass('open').should.equal(true);
1238-
1239- Y.one('body').simulate('click');
1240- indicator.ancestor().hasClass('open').should.equal(false);
1241-
1242- container.remove();
1243- done();
1244- });
1245-
1246 });
1247
1248
1249
1250=== modified file 'test/test_onboarding.js'
1251--- test/test_onboarding.js 2013-10-23 00:12:47 +0000
1252+++ test/test_onboarding.js 2013-11-13 18:41:54 +0000
1253@@ -158,6 +158,7 @@
1254 onboard.closeHandler({halt: function() {}});
1255 assert.equal(container.getComputedStyle('display'), 'none');
1256 assert.isFalse(env_help.hasClass('hidden'));
1257+ assert.equal(localStorage.getItem('force-onboarding'), '');
1258 });
1259
1260 it('should not be shown if seen before', function() {
1261@@ -171,5 +172,19 @@
1262 assert.isTrue(background instanceof Y.Node);
1263 });
1264
1265+ it('sets index to 0, clears out local storage on reset', function() {
1266+ var onboard = new OnboardingView({
1267+ container: container
1268+ }).render();
1269+ onboard.nextHandler({halt: function() {}});
1270+ assert.equal(onboard.onboardingIndex, 1);
1271+
1272+ localStorage.setItem('onboarding', true);
1273+
1274+ onboard.reset();
1275+ assert.equal(onboard.onboardingIndex, 0);
1276+ assert.equal(localStorage.getItem('onboarding'), '');
1277+ });
1278+
1279 });
1280 })();
1281
1282=== modified file 'undocumented'
1283--- undocumented 2013-10-02 16:26:20 +0000
1284+++ undocumented 2013-11-13 18:41:54 +0000
1285@@ -71,7 +71,6 @@
1286 app/views/notifications.js:42 "initializer"
1287 app/views/notifications.js:286 "render"
1288 app/views/notifications.js:173 "render"
1289-app/views/notifications.js:265 "close"
1290 app/views/databinding.js:81 "keyfunc"
1291 app/views/topology/panzoom.js:91 "renderSlider"
1292 app/views/topology/panzoom.js:265 "renderedHandler"

Subscribers

People subscribed via source and target branches