Merge lp:~rharding/juju-gui/qa-ant-onboarding into lp:juju-gui/experimental

Proposed by Richard Harding
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
Reviewer Review Type Date Requested Status
Juju GUI Hackers Pending
Review via email: mp+191208@code.launchpad.net

Description of the change

Add support for an initial onboarding walkthrough

- Submitting for Ant
- Under the feature flag :flags:/onboard

https://codereview.appspot.com/14700043/

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

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):
   A [revision details]
   M app/app.js
   A app/assets/images/close-inspector-click.png
   A app/assets/images/close-inspector-hover.png
   A app/assets/images/close-inspector-normal.png
   A app/assets/images/non-sprites/onboarding/2-service-blocks.png
   A app/assets/images/non-sprites/onboarding/3-inspector.png
   A app/assets/images/non-sprites/onboarding/3-service-block.png
   M app/index.html
   M app/modules-debug.js
   M app/store/env/base.js
   A app/templates/onboarding.handlebars
   A app/views/onboarding.js
   M lib/views/stylesheet.less

Revision history for this message
Richard Harding (rharding) wrote :
Download full text (4.2 KiB)

Feedback below.

https://codereview.appspot.com/14700043/diff/1/app/app.js
File app/app.js (right):

https://codereview.appspot.com/14700043/diff/1/app/app.js#newcode1095
app/app.js:1095:
remove this blank line

https://codereview.appspot.com/14700043/diff/1/app/app.js#newcode1129
app/app.js:1129: initialise_onboarding: function() {
initialize (I know it's a US/UK english thing, but initialize is used
throughout the app already)

https://codereview.appspot.com/14700043/diff/1/app/app.js#newcode1131
app/app.js:1131: if (!this._onboarding) {
this is where I'd add the feature flag check.

if (!this._onboarding && window.flags.onboard) { ...

https://codereview.appspot.com/14700043/diff/1/app/app.js#newcode1132
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://codereview.appspot.com/14700043/diff/1/app/app.js#newcode1140
app/app.js:1140: this._onboarding.close();
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://codereview.appspot.com/14700043/diff/1/app/store/env/base.js
File app/store/env/base.js (right):

https://codereview.appspot.com/14700043/diff/1/app/store/env/base.js#newcode170
app/store/env/base.js:170: 'onboardDismissed': {value: false},
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://codereview.appspot.com/14700043/diff/1/app/views/onboarding.js
File app/views/onboarding.js (right):

https://codereview.appspot.com/14700043/diff/1/app/views/onboarding.js#newcode31
app/views/onboarding.js:31: var onboardingIndex = 0;
this is specific to the current view. I think this should be an
attribute created or set on initialize, or maybe another ATTR.

https://codereview.appspot.com/14700043/diff/1/app/views/onboarding.js#newcode53
app/views/onboarding.js:53: '.onboarding-cross': {
can this not be done with css using :hover state?

https://codereview.appspot.com/14700043/diff/1/app/views/onboarding.js#newcode71
app/views/onboarding.js:71: this.close();
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://codereview.appspot.com/14700043/diff/1/app/views/onboarding.js#newcode94
app/views/onboarding.js:94: this.onboardingIndex = 0;
you set this.onboardingIndex here, but you use the unattached version
elsewhere?

https://codereview.appspot.com/14700043/diff/1/app/views/onboarding.js#newcode110
app/views/onboarding.js:110: onboardingIndex = onboardingIndex + 1;
this.onboardingIndex

https://codereview.appspot.com/14700043/diff/1/app/views/onboarding.js#newcode135
app/views/onboarding.js:135: crossHandler: function(ev) {
yea, I'd think this whole function could go away if the css was updated
per https://develop...

Read more...

Revision history for this message
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?

https://codereview.appspot.com/14700043/

1128. By Richard Harding

Fix the reference to the class

Revision history for this message
Anthony Dillon (ya-bo-ng) wrote :
Download full text (3.1 KiB)

Comments back to Rick's review. Any uncommented are complete.

https://codereview.appspot.com/14700043/diff/1/app/app.js
File app/app.js (right):

https://codereview.appspot.com/14700043/diff/1/app/app.js#newcode1140
app/app.js:1140: this._onboarding.close();
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://codereview.appspot.com/14700043/diff/1/app/store/env/base.js
File app/store/env/base.js (right):

https://codereview.appspot.com/14700043/diff/1/app/store/env/base.js#newcode170
app/store/env/base.js:170: 'onboardDismissed': {value: false},
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://codereview.appspot.com/14700043/diff/1/app/views/onboarding.js
File app/views/onboarding.js (right):

https://codereview.appspot.com/14700043/diff/1/app/views/onboarding.js#newcode53
app/views/onboarding.js:53: '.onboarding-cross': {
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://codereview.appspot.com/14700043/diff/1/app/views/onboarding.js#newcode71
app/views/onboarding.js:71: this.close();
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://codereview.appspot.com/14700043/diff/1/app/views/onboarding.js#newcode135
app/views/onboarding.js:135: crossHandler: function(ev) {
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://developer.mozilla.org/en-US/docs/Web/CSS/:hover

Required for sprite updates.

https://codereview.appspot.com/14700043/diff/1/lib/views/stylesheet.less
File lib/views/stylesheet.less (right):

https://codereview.appspot.com/14700043/diff/1/lib/views/stylesheet.less#newcode2043
lib/views/stylesheet.less:2043: &.state-1 {
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.

https://codereview.appspot.com/14700043/...

Read more...

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

Comment

https://codereview.appspot.com/14700043/diff/1/app/app.js
File app/app.js (right):

https://codereview.appspot.com/14700043/diff/1/app/app.js#newcode1140
app/app.js:1140: this._onboarding.close();
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.

https://codereview.appspot.com/14700043/

Revision history for this message
Anthony Dillon (ya-bo-ng) wrote :

https://codereview.appspot.com/14700043/diff/1/app/app.js
File app/app.js (right):

https://codereview.appspot.com/14700043/diff/1/app/app.js#newcode1140
app/app.js:1140: this._onboarding.close();
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.

https://codereview.appspot.com/14700043/

Revision history for this message
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 :)

https://codereview.appspot.com/14700043/

1129. By Richard Harding

Sync with upstream

1130. By Richard Harding

Lint

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

Sync

1132. By Richard Harding

Adjust

1133. By Richard Harding

Sync lint changes

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

LGTM, couple of small tweaks I think.

https://codereview.appspot.com/14700043/diff/15001/app/app.js
File app/app.js (right):

https://codereview.appspot.com/14700043/diff/15001/app/app.js#newcode84
app/app.js:84: onboarding: {
we manually run the view so this doesn't need to be here.

https://codereview.appspot.com/14700043/diff/15001/app/store/env/base.js
File app/store/env/base.js (right):

https://codereview.appspot.com/14700043/diff/15001/app/store/env/base.js#newcode170
app/store/env/base.js:170: 'onboardDismissed': {value: false},
is this used? If not can we get rid of this as well?

https://codereview.appspot.com/14700043/diff/15001/app/views/onboarding.js
File app/views/onboarding.js (right):

https://codereview.appspot.com/14700043/diff/15001/app/views/onboarding.js#newcode175
app/views/onboarding.js:175: this.onboardingIndex + 1, this.stateCount);
this should just be this.states.length

https://codereview.appspot.com/14700043/diff/15001/app/views/onboarding.js#newcode204
app/views/onboarding.js:204: this.stateCount = this.states.length - 1;
I'd just move this up to the incrementIndex since you don't need/use it
elsewhere.

https://codereview.appspot.com/14700043/diff/15001/test/test_onboarding.js
File test/test_onboarding.js (right):

https://codereview.appspot.com/14700043/diff/15001/test/test_onboarding.js#newcode159
test/test_onboarding.js:159: onboard.closeHandler({halt: function()
{}});
What you can do here is to do a onboardingCross.simulate('click') and it
should fire the click event and it'll close.

https://codereview.appspot.com/14700043/

Revision history for this message
Gary Poster (gary) wrote :
Download full text (3.8 KiB)

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://codereview.appspot.com/14700043/diff/15001/app/app.js
File app/app.js (right):

https://codereview.appspot.com/14700043/diff/15001/app/app.js#newcode1132
app/app.js:1132: if (path === '/' || path === '/:flags:/onboard/') {
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://codereview.appspot.com/14700043/diff/15001/app/store/env/base.js
File app/store/env/base.js (right):

https://codereview.appspot.com/14700043/diff/15001/app/store/env/base.js#newcode170
app/store/env/base.js:170: 'onboardDismissed': {value: false},
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://codereview.appspot.com/14700043/diff/15001/app/views/onboarding.js
File app/views/onboarding.js (right):

https://codereview.appspot.com/14700043/diff/15001/app/views/onboarding.js#newcode72
app/views/onboarding.js:72: this.close();
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://codereview.appspot.com/14700043/diff/15001/app/views/onboarding.js#newcode119
app/views/onboarding.js:119: * @method nextHandler
prevHandler

https://codereview.appspot.com/14700043/diff/15001/app/views/onboarding.js#newcode136
app/views/onboarding.js:136: crossHandler: function(ev) {
(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-click style wins over the -hover or
-normal styles, or that they get along somehow. I'll assume they do.

https://codereview.appspot.com/14700043/diff/15001/app/views/onboarding.js#newcode175
app/views/onboarding.js:175: this.onboardingIndex + 1, this.stateCount);
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://codereview.appspot.com/14700043/diff/15001/app/views/onboarding.js#newcode204
app/views/onboarding.js:204: this.stateCount = this.states.length - 1;
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://codereview.appspot.com/14700043/diff/15001/test/test_onboarding.js
File test/test_onboarding.js (right):

https://codereview.appspot.com/14700043/diff/15001/test/test_onboarding.js#newcode74
test/test_onboarding.js:74: assert.equal(onboard.onbo...

Read more...

Revision history for this message
Gary Poster (gary) wrote :

QA not ok, but if you apply the following patch and write associated
tests, it will be good.

http://paste.ubuntu.com/6247161/

This makes the flag handling much less fragile
(http://localhost:8888/sidebar/:flags:/charmworldv3/onboard/ shows
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!

https://codereview.appspot.com/14700043/

1134. By Richard Harding

Sync with trunk

1135. By Richard Harding

Sync with Ant

Revision history for this message
Richard Harding (rharding) wrote :
Revision history for this message
Anthony Dillon (ya-bo-ng) wrote :

https://codereview.appspot.com/14700043/diff/15001/app/views/onboarding.js
File app/views/onboarding.js (right):

https://codereview.appspot.com/14700043/diff/15001/app/views/onboarding.js#newcode136
app/views/onboarding.js:136: crossHandler: function(ev) {
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-click style wins over the -hover or
-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-click displays a brighter cross.

https://codereview.appspot.com/14700043/

1136. By Richard Harding

Add quick lint

Revision history for this message
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://codereview.appspot.com/14700043

https://codereview.appspot.com/14700043/

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-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'
60Binary 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'
62Binary 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'
64Binary 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'
67Binary 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'
69Binary 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'
71Binary 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+})();

Subscribers

People subscribed via source and target branches