Merge lp:~rharding/juju-gui/qa-ant-onboarding into lp:juju-gui/experimental
- qa-ant-onboarding
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 1140 |
Proposed branch: | lp:~rharding/juju-gui/qa-ant-onboarding |
Merge into: | lp:juju-gui/experimental |
Diff against target: |
717 lines (+613/-0) 8 files modified
app/app.js (+26/-0) app/index.html (+3/-0) app/modules-debug.js (+4/-0) app/templates/onboarding.handlebars (+33/-0) app/views/onboarding.js (+210/-0) lib/views/stylesheet.less (+172/-0) test/index.html (+1/-0) test/test_onboarding.js (+164/-0) |
To merge this branch: | bzr merge lp:~rharding/juju-gui/qa-ant-onboarding |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju GUI Hackers | Pending | ||
Review via email: mp+191208@code.launchpad.net |
Commit message
Description of the change
Add support for an initial onboarding walkthrough
- Submitting for Ant
- Under the feature flag :flags:/onboard
Richard Harding (rharding) wrote : | # |
Richard Harding (rharding) wrote : | # |
Feedback below.
https:/
File app/app.js (right):
https:/
app/app.js:1095:
remove this blank line
https:/
app/app.js:1129: initialise_
initialize (I know it's a US/UK english thing, but initialize is used
throughout the app already)
https:/
app/app.js:1131: if (!this._onboarding) {
this is where I'd add the feature flag check.
if (!this._onboarding && window.
https:/
app/app.js:1132: // Need to check onboarding exists due to the double
dispatch bug.
this comment kind of goes above this line where the if is.
https:/
app/app.js:1140: this._onboardin
why do we need to close here? If you hit the root url, and we render.
You close it with the button/control in the UI. If you're not at the /
url then we never show this and there's nothing to close?
https:/
File app/store/
https:/
app/store/
you had mentioned that it's not saving this value. This comes in to
where I'd create a valueFn that could check against your storage
location (session storage or localstorage) and pull the value out of
there. If it's not defined, then you show, else you don't show. The
render method would do a check when you hit it.
https:/
File app/views/
https:/
app/views/
this is specific to the current view. I think this should be an
attribute created or set on initialize, or maybe another ATTR.
https:/
app/views/
can this not be done with css using :hover state?
https:/
app/views/
is there any way to get back to the onboarding dialog after it's been
closed? Could this actually do a this.destroy() and remove itself from
the dom as well?
https:/
app/views/
you set this.onboarding
elsewhere?
https:/
app/views/
this.onboarding
https:/
app/views/
yea, I'd think this whole function could go away if the css was updated
per https:/
Richard Harding (rharding) wrote : | # |
QA Notes:
I didn't get the mask until I hit "Get Started" it seems like it would
mask to start out with?
Once when I got to the second step did it mask over the sidebar?
- 1128. By Richard Harding
-
Fix the reference to the class
Anthony Dillon (ya-bo-ng) wrote : | # |
Comments back to Rick's review. Any uncommented are complete.
https:/
File app/app.js (right):
https:/
app/app.js:1140: this._onboardin
On 2013/10/15 14:55:34, rharding wrote:
> why do we need to close here? If you hit the root url, and we render.
You close
> it with the button/control in the UI. If you're not at the / url then
we never
> show this and there's nothing to close?
This is used when the user use's the browsers navigation buttons to jump
form build and browse.
https:/
File app/store/
https:/
app/store/
On 2013/10/15 14:55:34, rharding wrote:
> you had mentioned that it's not saving this value. This comes in to
where I'd
> create a valueFn that could check against your storage location
(session storage
> or localstorage) and pull the value out of there. If it's not defined,
then you
> show, else you don't show. The render method would do a check when you
hit it.
I created this value in the storage for this purpose but did not manage
to set or get it from app.js when rendering.
https:/
File app/views/
https:/
app/views/
On 2013/10/15 14:55:34, rharding wrote:
> can this not be done with css using :hover state?
These events are used to update the spirited background image, therefore
need to update the class on the element.
https:/
app/views/
On 2013/10/15 14:55:34, rharding wrote:
> is there any way to get back to the onboarding dialog after it's been
closed?
> Could this actually do a this.destroy() and remove itself from the dom
as well?
There is going to be a drop down in the header that contains an option
to display the onboarding message again.
https:/
app/views/
On 2013/10/15 14:55:34, rharding wrote:
> yea, I'd think this whole function could go away if the css was
updated per
> https:/
Required for sprite updates.
https:/
File lib/views/
https:/
lib/views/
On 2013/10/15 14:55:34, rharding wrote:
> this seems like it could be done using a .active flag or something.
Where one of
> the divs is marked .active and .active picks up things like display
block and
> sets background color?
The reason I did it this way is because the different states control the
screen-mask which I outside the panels.
Richard Harding (rharding) wrote : | # |
Comment
https:/
File app/app.js (right):
https:/
app/app.js:1140: this._onboardin
On 2013/10/15 16:13:05, Ant wrote:
> On 2013/10/15 14:55:34, rharding wrote:
> > why do we need to close here? If you hit the root url, and we
render. You
> close
> > it with the button/control in the UI. If you're not at the / url
then we never
> > show this and there's nothing to close?
> This is used when the user use's the browsers navigation buttons to
jump form
> build and browse.
When they go from browse to build though the url changes and will no
longer be /. So I think it's safe to leave this out.
Anthony Dillon (ya-bo-ng) wrote : | # |
https:/
File app/app.js (right):
https:/
app/app.js:1140: this._onboardin
On 2013/10/15 16:27:00, rharding wrote:
> On 2013/10/15 16:13:05, Ant wrote:
> > On 2013/10/15 14:55:34, rharding wrote:
> > > why do we need to close here? If you hit the root url, and we
render. You
> > close
> > > it with the button/control in the UI. If you're not at the / url
then we
> never
> > > show this and there's nothing to close?
> >
> > This is used when the user use's the browsers navigation buttons to
jump form
> > build and browse.
> When they go from browse to build though the url changes and will no
longer be
> /. So I think it's safe to leave this out.
Ok, taking it out.
Anthony Dillon (ya-bo-ng) wrote : | # |
On 2013/10/15 15:00:22, rharding wrote:
> QA Notes:
> I didn't get the mask until I hit "Get Started" it seems like it would
mask to
> start out with?
> Once when I got to the second step did it mask over the sidebar?
That is a UX/design decision. It is the behaviour I was given to
implement. Testing may prove you right :)
- 1129. By Richard Harding
-
Sync with upstream
- 1130. By Richard Harding
-
Lint
Richard Harding (rharding) wrote : | # |
Please take a look.
- 1131. By Richard Harding
-
Sync
- 1132. By Richard Harding
-
Adjust
- 1133. By Richard Harding
-
Sync lint changes
Richard Harding (rharding) wrote : | # |
Please take a look.
Richard Harding (rharding) wrote : | # |
LGTM, couple of small tweaks I think.
https:/
File app/app.js (right):
https:/
app/app.js:84: onboarding: {
we manually run the view so this doesn't need to be here.
https:/
File app/store/
https:/
app/store/
is this used? If not can we get rid of this as well?
https:/
File app/views/
https:/
app/views/
this should just be this.states.length
https:/
app/views/
I'd just move this up to the incrementIndex since you don't need/use it
elsewhere.
https:/
File test/test_
https:/
test/test_
{}});
What you can do here is to do a onboardingCross
should fire the click event and it'll close.
Gary Poster (gary) wrote : | # |
LGTM if you change the route check code in app.js. I'll try to look up
the proper spelling and post it here in a few minutes. Everything else
is trivial and/or just suggestions or ideas.
https:/
File app/app.js (right):
https:/
app/app.js:1132: if (path === '/' || path === '/:flags:
Technically, this should use the routing code to get the path in the
default namespace. I'll try to dig up how to do that later.
I say "technically" because we are interested in removing the namespace
code. We aren't doing that right now, though, so using the routing's
parsing code is the right thing to do.
https:/
File app/store/
https:/
app/store/
On 2013/10/16 17:01:11, rharding wrote:
> is this used? If not can we get rid of this as well?
Looks like it is part of a nascent plan to persist the flag, but I agree
that we ought to keep this out until/unless it's used.
https:/
File app/views/
https:/
app/views/
I don't yet see the value of separating closeHandler and close. Even
for testing this doesn't seem like a huge win. OTOH, maybe I'm missing
something.
https:/
app/views/
prevHandler
https:/
app/views/
(1) this is also registered for mouseup. Why?
(2) Other than the blasted documentation blather :-D, this feels like it
would be cleaner as separate methods, one for each event. That's a
suggestion: take it or leave it.
(3) .replaceClass(..., ...) could make each type into single calls.
(4) I hope the close-inspector
-normal styles, or that they get along somehow. I'll assume they do.
https:/
app/views/
On 2013/10/16 17:01:11, rharding wrote:
> this should just be this.states.length
you mean, "this.stateCount" should be "this.states.length - 1". Sounds
good to me.
https:/
app/views/
On 2013/10/16 17:01:11, rharding wrote:
> I'd just move this up to the incrementIndex since you don't need/use
it
> elsewhere.
+1
https:/
File test/test_
https:/
test/test_
Gary Poster (gary) wrote : | # |
QA not ok, but if you apply the following patch and write associated
tests, it will be good.
http://
This makes the flag handling much less fragile
(http://
onboarding as it should, for instance) and disables onboarding for the
fullscreen view, where it will be broken. Please merge this and then
write a few associated tests.
Thank you!
- 1134. By Richard Harding
-
Sync with trunk
- 1135. By Richard Harding
-
Sync with Ant
Richard Harding (rharding) wrote : | # |
Please take a look.
Anthony Dillon (ya-bo-ng) wrote : | # |
https:/
File app/views/
https:/
app/views/
On 2013/10/16 17:52:32, gary.poster wrote:
> (1) this is also registered for mouseup. Why?
> (2) Other than the blasted documentation blather :-D, this feels like
it would
> be cleaner as separate methods, one for each event. That's a
suggestion: take
> it or leave it.
> (3) .replaceClass(..., ...) could make each type into single calls.
> (4) I hope the close-inspector
-normal
> styles, or that they get along somehow. I'll assume they do.
1. The mouse up event calls closeHandler
2. I'll leave this for now but happy to update if required
3. Brilliant, done.
4. The close-inspector
- 1136. By Richard Harding
-
Add quick lint
Richard Harding (rharding) wrote : | # |
*** Submitted:
Add support for an initial onboarding walkthrough
- Submitting for Ant
- Under the feature flag :flags:/onboard
R=Ant, gary.poster
CC=
https:/
Preview Diff
1 | === modified file 'app/app.js' |
2 | --- app/app.js 2013-10-17 00:17:47 +0000 |
3 | +++ app/app.js 2013-10-17 15:15:29 +0000 |
4 | @@ -372,6 +372,7 @@ |
5 | this.cookieHandler = null; |
6 | |
7 | this.renderEnvironment = true; |
8 | + |
9 | // If this property has a value other than '/' then |
10 | // navigate to it after logging in. |
11 | this.redirectPath = '/'; |
12 | @@ -1109,13 +1110,37 @@ |
13 | */ |
14 | callback: function() { |
15 | this.views.environment.instance.rendered(); |
16 | + this.initializeOnboarding(); |
17 | }, |
18 | render: true |
19 | }); |
20 | + |
21 | next(); |
22 | }, |
23 | |
24 | /** |
25 | + * Create a 'welcome' message walkthrough for new usersl. |
26 | + * |
27 | + * @method initialize_onboarding |
28 | + */ |
29 | + initializeOnboarding: function() { |
30 | + var path = window.location.pathname; |
31 | + // Need to check onboarding exists due to the double dispatch bug. |
32 | + if (!this._onboarding && window.flags.onboard) { |
33 | + if (path === '/' || path === '/:flags:/onboard/') { |
34 | + var paths = this.nsRouter.parse(window.location.toString()); |
35 | + path = paths.charmbrowser; |
36 | + if (path === '/sidebar/' || |
37 | + (this.get('defaultViewmode') === 'sidebar' && path === '/')) { |
38 | + this._onboarding = new Y.juju.views.OnboardingView( |
39 | + {'container': '#onboarding'}); |
40 | + this._onboarding.render(); |
41 | + } |
42 | + } |
43 | + } |
44 | + }, |
45 | + |
46 | + /** |
47 | * Object routing support |
48 | * |
49 | * This utility helps map from model objects to routes |
50 | @@ -1334,6 +1359,7 @@ |
51 | 'juju-views', |
52 | 'juju-view-environment', |
53 | 'juju-view-login', |
54 | + 'juju-view-onboarding', |
55 | 'juju-landscape', |
56 | 'juju-websocket-logging', |
57 | 'io', |
58 | |
59 | === added file 'app/assets/images/close-inspector-click.png' |
60 | Binary files app/assets/images/close-inspector-click.png 1970-01-01 00:00:00 +0000 and app/assets/images/close-inspector-click.png 2013-10-17 15:15:29 +0000 differ |
61 | === added file 'app/assets/images/close-inspector-hover.png' |
62 | Binary files app/assets/images/close-inspector-hover.png 1970-01-01 00:00:00 +0000 and app/assets/images/close-inspector-hover.png 2013-10-17 15:15:29 +0000 differ |
63 | === added file 'app/assets/images/close-inspector-normal.png' |
64 | Binary files app/assets/images/close-inspector-normal.png 1970-01-01 00:00:00 +0000 and app/assets/images/close-inspector-normal.png 2013-10-17 15:15:29 +0000 differ |
65 | === added directory 'app/assets/images/non-sprites/onboarding' |
66 | === added file 'app/assets/images/non-sprites/onboarding/2-service-blocks.png' |
67 | Binary files app/assets/images/non-sprites/onboarding/2-service-blocks.png 1970-01-01 00:00:00 +0000 and app/assets/images/non-sprites/onboarding/2-service-blocks.png 2013-10-17 15:15:29 +0000 differ |
68 | === added file 'app/assets/images/non-sprites/onboarding/3-inspector.png' |
69 | Binary files app/assets/images/non-sprites/onboarding/3-inspector.png 1970-01-01 00:00:00 +0000 and app/assets/images/non-sprites/onboarding/3-inspector.png 2013-10-17 15:15:29 +0000 differ |
70 | === added file 'app/assets/images/non-sprites/onboarding/3-service-block.png' |
71 | Binary files app/assets/images/non-sprites/onboarding/3-service-block.png 1970-01-01 00:00:00 +0000 and app/assets/images/non-sprites/onboarding/3-service-block.png 2013-10-17 15:15:29 +0000 differ |
72 | === modified file 'app/index.html' |
73 | --- app/index.html 2013-10-17 00:17:47 +0000 |
74 | +++ app/index.html 2013-10-17 15:15:29 +0000 |
75 | @@ -101,6 +101,9 @@ |
76 | </div> |
77 | </div> |
78 | </div> |
79 | + |
80 | + <div id="onboarding"></div> |
81 | + |
82 | <div class="cookie-policy" style="display:none;"> |
83 | <div class="wrapper"> |
84 | <a href="" class="link-cta">Close</a> |
85 | |
86 | === modified file 'app/modules-debug.js' |
87 | --- app/modules-debug.js 2013-10-02 16:26:20 +0000 |
88 | +++ app/modules-debug.js 2013-10-17 15:15:29 +0000 |
89 | @@ -249,6 +249,10 @@ |
90 | fullpath: '/juju-ui/views/login.js' |
91 | }, |
92 | |
93 | + 'juju-view-onboarding': { |
94 | + fullpath: '/juju-ui/views/onboarding.js' |
95 | + }, |
96 | + |
97 | 'juju-templates': { |
98 | fullpath: '/juju-ui/templates.js' |
99 | }, |
100 | |
101 | === added file 'app/templates/onboarding.handlebars' |
102 | --- app/templates/onboarding.handlebars 1970-01-01 00:00:00 +0000 |
103 | +++ app/templates/onboarding.handlebars 2013-10-17 15:15:29 +0000 |
104 | @@ -0,0 +1,33 @@ |
105 | + |
106 | +<div id="onboarding-background" class="state-0"> |
107 | + <div id="onboarding-screen-mask"></div> |
108 | + <div id="onboarding-message"> |
109 | + <a class="onboarding-cross sprite close-inspector-normal" href="" ></a> |
110 | + <div class="panel panel-0"> |
111 | + <h3 class="header type3">Welcome to Juju</h3> |
112 | + <p class="text type11">Juju enables you to configure, manage, maintain, deploy and scale efficiently with best-practice Charms on any cloud or your own Ubuntu laptop.</p> |
113 | + </div> |
114 | + <div class="panel panel-1"> |
115 | + <h3 class="header type3">Build your environment</h3> |
116 | + <p class="text type11">The Charm browser has hundreds of Charms allowing you to build complex services which can be visualised in Juju.</p> |
117 | + <p class=" text type11">Drag them from the Charm browser onto your canvas to start building your environments.</p> |
118 | + </div> |
119 | + <div class="panel panel-2"> |
120 | + <h3 class="header type3">Connect services</h3> |
121 | + <p class="text type11">Configure and deploy your services and connect them with intelligent relationship lines to build your environment.</p> |
122 | + <img src="/juju-ui/assets/images/non-sprites/onboarding/2-service-blocks.png" class="service-blocks" /> |
123 | + </div> |
124 | + <div class="panel panel-3"> |
125 | + <h3 class="header type3">Monitor and manage</h3> |
126 | + <p class="text type11">The Juju GUI inspector offers comprehensive tools that give you ability to scale out, triage errors and update your services.</p> |
127 | + <img src="/juju-ui/assets/images/non-sprites/onboarding/3-service-block.png" class="service-block" /> |
128 | + <img src="/juju-ui/assets/images/non-sprites/onboarding/3-inspector.png" class="inspector" /> |
129 | + </div> |
130 | + <ul> |
131 | + <li><a href="" class="onboarding-close">Get started</a></li> |
132 | + <li><a href="" class="onboarding-start highlight">Quick tour of Juju</a></li> |
133 | + <li><a href="" class="onboarding-next highlight">Next</a></li> |
134 | + <li><a href="" class="onboarding-prev">Prev</a></li> |
135 | + </ul> |
136 | + </div> |
137 | +</div> |
138 | \ No newline at end of file |
139 | |
140 | === added file 'app/views/onboarding.js' |
141 | --- app/views/onboarding.js 1970-01-01 00:00:00 +0000 |
142 | +++ app/views/onboarding.js 2013-10-17 15:15:29 +0000 |
143 | @@ -0,0 +1,210 @@ |
144 | +/* |
145 | +This file is part of the Juju GUI, which lets users view and manage Juju |
146 | +environments within a graphical interface (https://launchpad.net/juju-gui). |
147 | +Copyright (C) 2012-2013 Canonical Ltd. |
148 | + |
149 | +This program is free software: you can redistribute it and/or modify it under |
150 | +the terms of the GNU Affero General Public License version 3, as published by |
151 | +the Free Software Foundation. |
152 | + |
153 | +This program is distributed in the hope that it will be useful, but WITHOUT |
154 | +ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
155 | +SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero |
156 | +General Public License for more details. |
157 | + |
158 | +You should have received a copy of the GNU Affero General Public License along |
159 | +with this program. If not, see <http://www.gnu.org/licenses/>. |
160 | +*/ |
161 | + |
162 | +'use strict'; |
163 | + |
164 | +/** |
165 | + * Provide the OnboardingView class. |
166 | + * |
167 | + * @module onboarding |
168 | + * @submodule views.onboarding |
169 | + */ |
170 | + |
171 | +YUI.add('juju-view-onboarding', function(Y) { |
172 | + |
173 | + var views = Y.namespace('juju.views'); |
174 | + |
175 | + /** |
176 | + * The OnboardingView class. |
177 | + * |
178 | + * @class OnboardingView |
179 | + */ |
180 | + views.OnboardingView = Y.Base.create('OnboardingView', Y.View, [], { |
181 | + template: views.Templates.onboarding, |
182 | + events: { |
183 | + '.onboarding-close': { |
184 | + click: 'closeHandler' |
185 | + }, |
186 | + '.onboarding-start': { |
187 | + click: 'nextHandler' |
188 | + }, |
189 | + '.onboarding-next': { |
190 | + click: 'nextHandler' |
191 | + }, |
192 | + '.onboarding-prev': { |
193 | + click: 'prevHandler' |
194 | + }, |
195 | + '.onboarding-cross': { |
196 | + mousedown: 'crossHandler', |
197 | + mouseout: 'crossHandler', |
198 | + mouseover: 'crossHandler', |
199 | + mouseup: 'closeHandler' |
200 | + } |
201 | + }, |
202 | + onboardingIndex: 0, |
203 | + states: ['state-0', 'state-1', 'state-2', 'state-3'], |
204 | + |
205 | + /** |
206 | + * Onboarding event handler. When clicking the close button, |
207 | + * remove the onboarding and put the user back into the environment, |
208 | + * |
209 | + * @method closeHandler |
210 | + * @param {Object} ev An event object (with a "currentTarget" attribute). |
211 | + * @return {undefined} Mutates only. |
212 | + */ |
213 | + closeHandler: function(ev) { |
214 | + ev.halt(); |
215 | + var container = this.get('container'); |
216 | + container.hide(); |
217 | + Y.one('#environment-help').removeClass('hidden'); |
218 | + }, |
219 | + |
220 | + /** |
221 | + * Show the container and hide the enviroment help. |
222 | + * |
223 | + * @method open |
224 | + * @return {undefined} Mutates only. |
225 | + */ |
226 | + open: function() { |
227 | + this.onboardingIndex = 0; |
228 | + this.drawContent(); |
229 | + this.onboarding.show(); |
230 | + Y.one('#environment-help').addClass('hidden'); |
231 | + }, |
232 | + |
233 | + /** |
234 | + * Onboarding event handler. When clicking the next button, |
235 | + * update the index counter, and update to the next step of onboarding. |
236 | + * |
237 | + * @method nextHandler |
238 | + * @param {Object} ev An event object (with a "currentTarget" attribute). |
239 | + * @return {undefined} Mutates only. |
240 | + */ |
241 | + nextHandler: function(ev) { |
242 | + ev.halt(); |
243 | + this.incrementIndex(); |
244 | + this.drawContent(); |
245 | + }, |
246 | + |
247 | + /** |
248 | + * Onboarding event handler. When clicking the prev button, |
249 | + * update the index counter, and update to the prev step of onboarding. |
250 | + * |
251 | + * @method prevHandler |
252 | + * @param {Object} ev An event object (with a "currentTarget" attribute). |
253 | + * @return {undefined} Mutates only. |
254 | + */ |
255 | + prevHandler: function(ev) { |
256 | + ev.halt(); |
257 | + this.decrementIndex(); |
258 | + this.drawContent(); |
259 | + }, |
260 | + |
261 | + /** |
262 | + * Onboarding event handler. When hover and click the close cross. |
263 | + * |
264 | + * @method crossHandler |
265 | + * @param {Object} ev An event object (with a "currentTarget" attribute). |
266 | + * @return {undefined} Mutates only. |
267 | + */ |
268 | + crossHandler: function(ev) { |
269 | + var container = this.get('container'); |
270 | + var close_button = container.one('.onboarding-cross'); |
271 | + switch (ev._event.type) { |
272 | + case 'mouseover': |
273 | + close_button.replaceClass('close-inspector-normal', |
274 | + 'close-inspector-hover'); |
275 | + break; |
276 | + case 'mouseout': |
277 | + close_button.replaceClass('close-inspector-hover', |
278 | + 'close-inspector-normal'); |
279 | + break; |
280 | + case 'mousedown': |
281 | + close_button.replaceClass('close-inspector-hover', |
282 | + 'close-inspector-click'); |
283 | + break; |
284 | + } |
285 | + }, |
286 | + |
287 | + /** |
288 | + * @method drawContent |
289 | + * @return {undefined} Mutates only. |
290 | + */ |
291 | + drawContent: function() { |
292 | + var container = this.get('container'); |
293 | + var container_bg = container.one('#onboarding-background'); |
294 | + |
295 | + this.states.forEach(function(state, idx) { |
296 | + container_bg.removeClass(state); |
297 | + }); |
298 | + container_bg.addClass(this.states[this.onboardingIndex]); |
299 | + }, |
300 | + |
301 | + /** |
302 | + * @method incrementIndex |
303 | + * @return {undefined} Mutates only. |
304 | + */ |
305 | + incrementIndex: function() { |
306 | + this.onboardingIndex = Math.min( |
307 | + this.onboardingIndex + 1, this.states.length - 1); |
308 | + }, |
309 | + |
310 | + /** |
311 | + * @method decrementIndex |
312 | + * @return {undefined} Mutates only. |
313 | + */ |
314 | + decrementIndex: function() { |
315 | + this.onboardingIndex = Math.max(this.onboardingIndex - 1, 0); |
316 | + |
317 | + }, |
318 | + |
319 | + /** |
320 | + * Render the page. |
321 | + * |
322 | + * Reveal the mask element, and show the onboarding window. |
323 | + * |
324 | + * @method render |
325 | + * @return {undefined} Mutates only. |
326 | + */ |
327 | + render: function() { |
328 | + // In order to have the mask cover everything, it needs to be an |
329 | + // immediate child of the body. In order for it to render immediately |
330 | + // when the app loads, it needs to be in index.html. |
331 | + this.onboarding = Y.one('body > #onboarding'); |
332 | + if (!this.onboarding) { |
333 | + // No mask in the DOM, as is the case in tests. |
334 | + return this; |
335 | + } |
336 | + |
337 | + this.get('container').setHTML(this.template()); |
338 | + this.open(); |
339 | + |
340 | + return this; |
341 | + } |
342 | + |
343 | + }); |
344 | + |
345 | + |
346 | +}, '0.1.0', { |
347 | + requires: [ |
348 | + 'juju-templates', |
349 | + 'juju-view-utils', |
350 | + 'node', |
351 | + 'view' |
352 | + ] |
353 | +}); |
354 | |
355 | === modified file 'lib/views/stylesheet.less' |
356 | --- lib/views/stylesheet.less 2013-10-17 00:17:47 +0000 |
357 | +++ lib/views/stylesheet.less 2013-10-17 15:15:29 +0000 |
358 | @@ -2003,6 +2003,178 @@ |
359 | font-size: 8pt; |
360 | } |
361 | } |
362 | +#onboarding-background { |
363 | + display: block; |
364 | + position: absolute; |
365 | + top: 0; |
366 | + left: 0; |
367 | + width: 100%; |
368 | + height: 100%; |
369 | + z-index: 9999; |
370 | + |
371 | + #onboarding-screen-mask { |
372 | + display: block; |
373 | + position: absolute; |
374 | + top: 80px; |
375 | + left: 0; |
376 | + width: 100%; |
377 | + height: 100%; |
378 | + z-index: 10000; |
379 | + } |
380 | + |
381 | + #onboarding-message { |
382 | + .create-border-radius(@border-radius); |
383 | + display: block; |
384 | + z-index: 10001; |
385 | + position: relative; |
386 | + top: 100px; |
387 | + left: 350px; |
388 | + width: 320px; |
389 | + background-color: @inspector-background-color; |
390 | + padding: 20px; |
391 | + |
392 | + .onboarding-cross { |
393 | + float: right; |
394 | + } |
395 | + |
396 | + .header { |
397 | + margin-bottom: 20px; |
398 | + } |
399 | + |
400 | + .header, |
401 | + .text { |
402 | + color: @charm-panel-configure-title-color; |
403 | + } |
404 | + |
405 | + img { |
406 | + position: absolute; |
407 | + } |
408 | + |
409 | + .panel { |
410 | + display: none; |
411 | + height: 169px; |
412 | + } |
413 | + |
414 | + ul { |
415 | + position: relative; |
416 | + list-style: none; |
417 | + margin-left: 0; |
418 | + height: 57px; |
419 | + |
420 | + li { |
421 | + display: inline; |
422 | + } |
423 | + |
424 | + a { |
425 | + @box-shadow: inset 0 2px 2px rgba(0, 0, 0, 0.3); |
426 | + .create-box-shadow(@box-shadow); |
427 | + .create-border-radius(@bws-corner); |
428 | + background-color: @inspector-dark; |
429 | + display: inline-block; |
430 | + height: 40px; |
431 | + margin-top: 17px; |
432 | + padding: 0 30px; |
433 | + color: #fff; |
434 | + line-height: 40px; |
435 | + |
436 | + &.onboarding-next, |
437 | + &.onboarding-start, |
438 | + &.onboarding-prev { |
439 | + float: right; |
440 | + } |
441 | + &.highlight { |
442 | + background-color: @charm-panel-orange; |
443 | + margin-left: 10px; |
444 | + } |
445 | + } |
446 | + } |
447 | + } |
448 | + |
449 | + &.state-0 { |
450 | + #onboarding-message { |
451 | + .panel-0 { |
452 | + display: block; |
453 | + } |
454 | + ul a.onboarding-next, |
455 | + ul a.onboarding-prev { |
456 | + display: none; |
457 | + } |
458 | + } |
459 | + |
460 | + } |
461 | + |
462 | + &.state-1 { |
463 | + #onboarding-screen-mask { |
464 | + left: 290px; |
465 | + background-color: rgba(0, 0, 0, 0.75); |
466 | + } |
467 | + #onboarding-message { |
468 | + .panel-1 { |
469 | + display: block; |
470 | + } |
471 | + ul a.onboarding-next { |
472 | + display: inline-block; |
473 | + } |
474 | + ul a.onboarding-start { |
475 | + display: none; |
476 | + } |
477 | + } |
478 | + } |
479 | + |
480 | + &.state-2 { |
481 | + #onboarding-screen-mask { |
482 | + left: 0; |
483 | + background-color: rgba(0, 0, 0, 0.75); |
484 | + } |
485 | + #onboarding-message { |
486 | + .panel-2 { |
487 | + display: block; |
488 | + } |
489 | + ul a.onboarding-next { |
490 | + display: inline-block; |
491 | + } |
492 | + ul a.onboarding-start { |
493 | + display: none; |
494 | + } |
495 | + .service-blocks { |
496 | + top: 210px; |
497 | + left: 125px; |
498 | + } |
499 | + } |
500 | + } |
501 | + |
502 | + &.state-3 { |
503 | + #onboarding-screen-mask { |
504 | + left: 0; |
505 | + background-color: rgba(0, 0, 0, 0.75); |
506 | + } |
507 | + #onboarding-message { |
508 | + .panel-3 { |
509 | + display: block; |
510 | + } |
511 | + ul a.onboarding-close { |
512 | + background-color: @charm-panel-orange; |
513 | + margin-left: 10px; |
514 | + float: right; |
515 | + } |
516 | + ul a.onboarding-next { |
517 | + display: none; |
518 | + } |
519 | + ul a.onboarding-start { |
520 | + display: none; |
521 | + } |
522 | + .service-block { |
523 | + top: 320px; |
524 | + left: 90px; |
525 | + } |
526 | + .inspector { |
527 | + right: -370px; |
528 | + top: 0; |
529 | + } |
530 | + } |
531 | + } |
532 | + |
533 | +} |
534 | .alert { |
535 | letter-spacing: normal; |
536 | } |
537 | |
538 | === modified file 'test/index.html' |
539 | --- test/index.html 2013-10-08 23:14:08 +0000 |
540 | +++ test/index.html 2013-10-17 15:15:29 +0000 |
541 | @@ -91,6 +91,7 @@ |
542 | <script src="test_model.js"></script> |
543 | <script src="test_model_bundle.js"></script> |
544 | <script src="test_model_controller.js"></script> |
545 | + <script src="test_onboarding.js"></script> |
546 | <script src="test_topology.js"></script> |
547 | <script src="test_topology_relation.js"></script> |
548 | |
549 | |
550 | === added file 'test/test_onboarding.js' |
551 | --- test/test_onboarding.js 1970-01-01 00:00:00 +0000 |
552 | +++ test/test_onboarding.js 2013-10-17 15:15:29 +0000 |
553 | @@ -0,0 +1,164 @@ |
554 | +/* |
555 | +This file is part of the Juju GUI, which lets users view and manage Juju |
556 | +environments within a graphical interface (https://launchpad.net/juju-gui). |
557 | +Copyright (C) 2012-2013 Canonical Ltd. |
558 | + |
559 | +This program is free software: you can redistribute it and/or modify it under |
560 | +the terms of the GNU Affero General Public License version 3, as published by |
561 | +the Free Software Foundation. |
562 | + |
563 | +This program is distributed in the hope that it will be useful, but WITHOUT |
564 | +ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
565 | +SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero |
566 | +General Public License for more details. |
567 | + |
568 | +You should have received a copy of the GNU Affero General Public License along |
569 | +with this program. If not, see <http://www.gnu.org/licenses/>. |
570 | +*/ |
571 | + |
572 | +'use strict'; |
573 | + |
574 | + |
575 | +(function() { |
576 | + |
577 | + describe('onboarding process', function() { |
578 | + var container, env_help, OnboardingView, views, utils, Y; |
579 | + |
580 | + before(function(done) { |
581 | + Y = YUI(GlobalConfig).use( |
582 | + 'node-event-simulate', |
583 | + 'juju-tests-utils', |
584 | + 'juju-view-onboarding', |
585 | + function(Y) { |
586 | + utils = Y.namespace('juju-tests.utils'); |
587 | + views = Y.namespace('juju.views'); |
588 | + OnboardingView = views.OnboardingView; |
589 | + done(); |
590 | + }); |
591 | + }); |
592 | + |
593 | + beforeEach(function() { |
594 | + container = utils.makeContainer('onboarding'); |
595 | + env_help = utils.makeContainer('environment-help'); |
596 | + window.flags.onboard = true; |
597 | + }); |
598 | + |
599 | + afterEach(function() { |
600 | + container.remove(true); |
601 | + env_help.remove(true); |
602 | + window.flags = {}; |
603 | + }); |
604 | + |
605 | + after(function() { |
606 | + |
607 | + }); |
608 | + |
609 | + it('should exist in the views namespace', function() { |
610 | + assert(views.OnboardingView); |
611 | + }); |
612 | + |
613 | + it('inits values correctly', function() { |
614 | + var onboard = new OnboardingView(); |
615 | + assert.equal(onboard.onboardingIndex, 0); |
616 | + }); |
617 | + |
618 | + it('increments screen index correctly', function() { |
619 | + var onboard = new OnboardingView({ |
620 | + container: container |
621 | + }); |
622 | + |
623 | + onboard.render(); |
624 | + onboard.nextHandler({halt: function() {}}); |
625 | + assert.equal(onboard.onboardingIndex, 1); |
626 | + onboard.nextHandler({halt: function() {}}); |
627 | + assert.equal(onboard.onboardingIndex, 2); |
628 | + }); |
629 | + |
630 | + it('decrements screen index correctly', function() { |
631 | + var onboard = new OnboardingView({ |
632 | + container: container |
633 | + }); |
634 | + |
635 | + onboard.render(); |
636 | + onboard.onboardingIndex = 2; |
637 | + onboard.prevHandler({halt: function() {}}); |
638 | + assert.equal(onboard.onboardingIndex, 1); |
639 | + onboard.prevHandler({halt: function() {}}); |
640 | + assert.equal(onboard.onboardingIndex, 0); |
641 | + }); |
642 | + |
643 | + it('renders correctly on first load', function() { |
644 | + var onboard = new OnboardingView({ |
645 | + container: container |
646 | + }); |
647 | + |
648 | + onboard.render(); |
649 | + var background = container.one('#onboarding-background'); |
650 | + assert.isTrue(background instanceof Y.Node); |
651 | + assert.isTrue(env_help.hasClass('hidden')); |
652 | + assert.equal(container.getComputedStyle('display'), 'block'); |
653 | + assert.isTrue(background.hasClass('state-0')); |
654 | + }); |
655 | + |
656 | + it('updates background css properly', function() { |
657 | + var onboard = new OnboardingView({ |
658 | + container: container |
659 | + }); |
660 | + |
661 | + onboard.render(); |
662 | + var background = container.one('#onboarding-background'); |
663 | + onboard.nextHandler({halt: function() {}}); |
664 | + assert.isTrue(background.hasClass('state-1'), 'should be state 1'); |
665 | + onboard.nextHandler({halt: function() {}}); |
666 | + assert.isTrue(background.hasClass('state-2'), 'should be 2'); |
667 | + onboard.prevHandler({halt: function() {}}); |
668 | + assert.isTrue(background.hasClass('state-1'), 'should be 1 again'); |
669 | + onboard.prevHandler({halt: function() {}}); |
670 | + assert.isTrue(background.hasClass('state-0'), 'should be 0 again'); |
671 | + onboard.prevHandler({halt: function() {}}); |
672 | + assert.isTrue(background.hasClass('state-0'), 'should stick to 0'); |
673 | + }); |
674 | + |
675 | + it('displays correct stage of onboarding', function() { |
676 | + var onboard = new OnboardingView({ |
677 | + container: container |
678 | + }); |
679 | + |
680 | + onboard.render(); |
681 | + |
682 | + var panel0 = container.one('.panel-0'); |
683 | + var panel1 = container.one('.panel-1'); |
684 | + var panel2 = container.one('.panel-2'); |
685 | + assert.equal( |
686 | + panel0.getComputedStyle('display'), 'block'); |
687 | + onboard.nextHandler({halt: function() {}}); |
688 | + assert.equal( |
689 | + panel1.getComputedStyle('display'), 'block'); |
690 | + onboard.nextHandler({halt: function() {}}); |
691 | + assert.equal( |
692 | + panel2.getComputedStyle('display'), 'block'); |
693 | + onboard.prevHandler({halt: function() {}}); |
694 | + assert.equal( |
695 | + panel1.getComputedStyle('display'), 'block'); |
696 | + onboard.prevHandler({halt: function() {}}); |
697 | + assert.equal( |
698 | + panel0.getComputedStyle('display'), 'block'); |
699 | + onboard.prevHandler({halt: function() {}}); |
700 | + assert.equal( |
701 | + panel0.getComputedStyle('display'), 'block'); |
702 | + }); |
703 | + |
704 | + it('closes onboarding', function() { |
705 | + var onboard = new OnboardingView({ |
706 | + container: container |
707 | + }); |
708 | + |
709 | + onboard.render(); |
710 | + var onboardingCross = container.one('.onboarding-cross'); |
711 | + assert.isTrue(onboardingCross instanceof Y.Node); |
712 | + onboard.closeHandler({halt: function() {}}); |
713 | + assert.equal(container.getComputedStyle('display'), 'none'); |
714 | + assert.isFalse(env_help.hasClass('hidden')); |
715 | + }); |
716 | + }); |
717 | +})(); |
Reviewers: mp+191208_ code.launchpad. net,
Message:
Please take a look.
Description:
Add support for an initial onboarding walkthrough
- Submitting for Ant
https:/ /code.launchpad .net/~rharding/ juju-gui/ qa-ant- onboarding/ +merge/ 191208
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/14700043/
Affected files (+447, --6 lines): images/ close-inspector -click. png images/ close-inspector -hover. png images/ close-inspector -normal. png images/ non-sprites/ onboarding/ 2-service- blocks. png images/ non-sprites/ onboarding/ 3-inspector. png images/ non-sprites/ onboarding/ 3-service- block.png debug.js env/base. js onboarding. handlebars onboarding. js stylesheet. less
A [revision details]
M app/app.js
A app/assets/
A app/assets/
A app/assets/
A app/assets/
A app/assets/
A app/assets/
M app/index.html
M app/modules-
M app/store/
A app/templates/
A app/views/
M lib/views/