Merge lp:~tveronezi/juju-gui/hotkeys into lp:juju-gui/experimental

Proposed by Thiago Veronezi
Status: Merged
Merged at revision: 254
Proposed branch: lp:~tveronezi/juju-gui/hotkeys
Merge into: lp:juju-gui/experimental
Diff against target: 191 lines (+146/-1)
4 files modified
app/app.js (+79/-1)
app/index.html (+3/-0)
test/index.html (+1/-0)
test/test_app_hotkeys.js (+63/-0)
To merge this branch: bzr merge lp:~tveronezi/juju-gui/hotkeys
Reviewer Review Type Date Requested Status
Benjamin Saller (community) Needs Fixing
Review via email: mp+131382@code.launchpad.net

Description of the change

application hotkeys

The user can hit alt+e to open the environment view and alt+s to focus the
charm search field.

https://codereview.appspot.com/6849078/

To post a comment you must log in.
Revision history for this message
Benjamin Saller (bcsaller) wrote :

It doesn't look like you used lbox for this, but I'm including a reply here.

http://yuilibrary.com/yui/docs/event/key.html has a key filtering spec

such that we can say things like

// require modifier keys with +(modifier)
Y.one('doc').on('key', composeMail, 'n+ctrl');

This seems to meet our needs here and will eliminate much of the code included in this diff. This way we can fire directly to handlers w/o multiple events per keydown to do indirection like you have now.

Looking forward to having keyboard shortcuts :)

review: Needs Fixing
Revision history for this message
Thiago Veronezi (tveronezi) wrote :

>>It doesn't look like you used lbox for this, but I'm including a reply
here.
Hmmm... strange. I think I did. Well... I will try again later.

>>Y.one('doc').on('key', composeMail, 'n+ctrl')
Ah! That's good. I used something I did for another project. I will switch
to the yui stuff. Thanks!

[]s,
Thiago.

On Thu, Oct 25, 2012 at 11:42 AM, Benjamin Saller <
<email address hidden>> wrote:

> Review: Needs Fixing
>
> It doesn't look like you used lbox for this, but I'm including a reply
> here.
>
> http://yuilibrary.com/yui/docs/event/key.html has a key filtering spec
>
> such that we can say things like
>
> // require modifier keys with +(modifier)
> Y.one('doc').on('key', composeMail, 'n+ctrl');
>
> This seems to meet our needs here and will eliminate much of the code
> included in this diff. This way we can fire directly to handlers w/o
> multiple events per keydown to do indirection like you have now.
>
> Looking forward to having keyboard shortcuts :)
>
> --
> https://code.launchpad.net/~tveronezi/juju-gui/hotkeys/+merge/131382
> You are the owner of lp:~tveronezi/juju-gui/hotkeys.
>

lp:~tveronezi/juju-gui/hotkeys updated
210. By Thiago Veronezi

trunk merge

211. By Thiago Veronezi

trunk merge

212. By Thiago Veronezi

personal code review

Revision history for this message
Thiago Veronezi (tveronezi) wrote :

Please take a look.

Revision history for this message
Thiago Veronezi (tveronezi) wrote :

Reviewers: mp+131382_code.launchpad.net,

Message:
Please take a look.

Description:
application hotkeys

The user can hit alt+e to open the environment view and alt+s to focus
the
charm search field.

https://code.launchpad.net/~tveronezi/juju-gui/hotkeys/+merge/131382

(do not edit description out of merge proposal)

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

Affected files:
   A [revision details]
   M app/app.js
   M app/index.html
   M test/index.html
   A test/test_app_hotkeys.js

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

Thanks for the branch, Thiago. I have a few minors, but one regression
that I'd like to see taken care of before I give a +1.

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

https://codereview.appspot.com/6849078/diff/1/app/app.js#newcode128
app/app.js:128: // F1...F12 or esc
Minor: This was unclear to me at first, and I had to stare at the next
line for a while to understand. What you're testing for is any key that
is NOT a function key or esc without a modifier, correct? I think that
this might be worth clarifying.

https://codereview.appspot.com/6849078/diff/1/app/app.js#newcode145
app/app.js:145: this.show_environment();
I'm not sure this is the right way to go about this, as it causes a
regression. If I view a service, then hit alt+E, the environment view
is shown, but not navigated to, leaving the url, in my case,
http://localhost:8888/service/mysql/. This breaks back-button behavior.

* view one service, mysql
* hit alt+E
* view another service, wordpress
* hit back: get taken to viewing mysql, because that was the last thing
in the URL stack.

https://codereview.appspot.com/6849078/diff/1/app/app.js#newcode557
app/app.js:557: if (next) {
Perhaps Y.Lang.isFunction(next), here?

https://codereview.appspot.com/6849078/

Revision history for this message
Thiago Veronezi (tveronezi) wrote :

Thanks for your review, Matt!

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

https://codereview.appspot.com/6849078/diff/1/app/app.js#newcode128
app/app.js:128: // F1...F12 or esc
On 2012/11/19 18:36:51, matthew.scott wrote:
> Minor: This was unclear to me at first, and I had to stare at the next
line for
> a while to understand. What you're testing for is any key that is NOT
a
> function key or esc without a modifier, correct? I think that this
might be
> worth clarifying.

Done.

https://codereview.appspot.com/6849078/diff/1/app/app.js#newcode145
app/app.js:145: this.show_environment();
On 2012/11/19 18:36:51, matthew.scott wrote:
> I'm not sure this is the right way to go about this, as it causes a
regression.
> If I view a service, then hit alt+E, the environment view is shown,
but not
> navigated to, leaving the url, in my case,
http://localhost:8888/service/mysql/.
> This breaks back-button behavior.

> * view one service, mysql
> * hit alt+E
> * view another service, wordpress
> * hit back: get taken to viewing mysql, because that was the last
thing in the
> URL stack.

Good catch. Thanks.
I've solved this issue by firing a 'navidateTo' event. So now instead of
this.show_environment(), we call this.fire('navigateTo', { url: '/' });

https://codereview.appspot.com/6849078/diff/1/app/app.js#newcode557
app/app.js:557: if (next) {
On 2012/11/19 18:36:51, matthew.scott wrote:
> Perhaps Y.Lang.isFunction(next), here?

Hmmm. Not shure. I just want to test if we have the next object (for
unit tests purposes). If the developer passes one thing other than a
function, we should just let the exception be thrown. wdyt?

https://codereview.appspot.com/6849078/

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

Thanks for the reply! +1 with the navigateTo change

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

https://codereview.appspot.com/6849078/diff/1/app/app.js#newcode145
app/app.js:145: this.show_environment();
On 2012/11/19 19:07:54, thiago wrote:
> On 2012/11/19 18:36:51, matthew.scott wrote:
> > I'm not sure this is the right way to go about this, as it causes a
> regression.
> > If I view a service, then hit alt+E, the environment view is shown,
but not
> > navigated to, leaving the url, in my case,
> http://localhost:8888/service/mysql/.
> > This breaks back-button behavior.
> >
> > * view one service, mysql
> > * hit alt+E
> > * view another service, wordpress
> > * hit back: get taken to viewing mysql, because that was the last
thing in the
> > URL stack.

> Good catch. Thanks.
> I've solved this issue by firing a 'navidateTo' event. So now instead
of
> this.show_environment(), we call this.fire('navigateTo', { url: '/'
});

Plugged this in, works well. Thanks!

https://codereview.appspot.com/6849078/diff/1/app/app.js#newcode557
app/app.js:557: if (next) {
On 2012/11/19 19:07:54, thiago wrote:
> On 2012/11/19 18:36:51, matthew.scott wrote:
> > Perhaps Y.Lang.isFunction(next), here?

> Hmmm. Not shure. I just want to test if we have the next object (for
unit tests
> purposes). If the developer passes one thing other than a function, we
should
> just let the exception be thrown. wdyt?

I don't feel that strongly either way :) Feel free to keep it as is,
unless someone else minds.

https://codereview.appspot.com/6849078/

Revision history for this message
Benjamin Saller (bcsaller) wrote :

With the navigate change this looks fine. I might remove the //XXX:
comment at the top of activate keys as even though we were not able to
use the YUI framework for this it is working code and working code
doesn't need to cry out for developer attention.

I'd also add a card indicating that we need design on a page bound to
'?'|'h' or similar that can list the hotkeys (and prehaps a link to help
in the base application template). These are useful but not
discoverable.

With that card in place, +1

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

https://codereview.appspot.com/6849078/diff/1/app/app.js#newcode557
app/app.js:557: if (next) {
On 2012/11/19 19:07:54, thiago wrote:
> On 2012/11/19 18:36:51, matthew.scott wrote:
> > Perhaps Y.Lang.isFunction(next), here?

> Hmmm. Not shure. I just want to test if we have the next object (for
unit tests
> purposes). If the developer passes one thing other than a function, we
should
> just let the exception be thrown. wdyt?

This is the 'next' route in the App.Router chain, the persistent views
need this to continue processing, but they'd either all need an 'if
(next) next();' guard or not.

https://codereview.appspot.com/6849078/

lp:~tveronezi/juju-gui/hotkeys updated
213. By Thiago Veronezi

code review

Revision history for this message
Thiago Veronezi (tveronezi) wrote :

Thanks for your review Ben!

[]s,
Thiago.

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

https://codereview.appspot.com/6849078/diff/1/app/app.js#newcode557
app/app.js:557: if (next) {
On 2012/11/20 13:26:32, benjamin.saller wrote:
> On 2012/11/19 19:07:54, thiago wrote:
> > On 2012/11/19 18:36:51, matthew.scott wrote:
> > > Perhaps Y.Lang.isFunction(next), here?
> >
> > Hmmm. Not shure. I just want to test if we have the next object (for
unit
> tests
> > purposes). If the developer passes one thing other than a function,
we should
> > just let the exception be thrown. wdyt?

> This is the 'next' route in the App.Router chain, the persistent views
need this
> to continue processing, but they'd either all need an 'if (next)
next();' guard
> or not.

I fact, I dont need it now that I am calling the event instead of the
show_environment method directly. I will remove it.

https://codereview.appspot.com/6849078/

Revision history for this message
Thiago Veronezi (tveronezi) wrote :

*** Submitted:

application hotkeys

The user can hit alt+e to open the environment view and alt+s to focus
the
charm search field.

R=matthew.scott, benjamin.saller
CC=
https://codereview.appspot.com/6849078

https://codereview.appspot.com/6849078/

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 2012-11-16 08:23:47 +0000
3+++ app/app.js 2012-11-20 14:53:22 +0000
4@@ -106,6 +106,85 @@
5 },
6
7 /**
8+ * This method activates the keyboard listeners.
9+ */
10+ activateHotkeys: function() {
11+ Y.one(window).on('keydown', function(ev) {
12+ var key = [],
13+ keyStr = null,
14+ data = { preventDefault: false };
15+ if (ev.altKey) {
16+ key.push('alt');
17+ } else if (ev.ctrlKey) {
18+ key.push('ctrl');
19+ } else if (ev.shiftKey) {
20+ key.push('shift');
21+ }
22+ if (key.length === 0 &&
23+ // If we have no modifier, check if this is a function or the esc
24+ // key. It it is not one of these keys, just do nothing.
25+ !(ev.keyCode >= 112 && ev.keyCode <= 123 || ev.keyCode === 27)) {
26+ return; //nothing to do
27+ }
28+ keyStr = keyCodeToString(ev.keyCode);
29+ if (!keyStr) {
30+ keyStr = ev.keyCode;
31+ }
32+ key.push(keyStr);
33+ Y.fire('window-' + key.join('-') + '-pressed', data);
34+ if (data.preventDefault) {
35+ ev.preventDefault();
36+ }
37+ });
38+
39+ Y.detachAll('window-alt-E-pressed');
40+ Y.on('window-alt-E-pressed', function(data) {
41+ this.fire('navigateTo', { url: '/' });
42+ data.preventDefault = true;
43+ }, this);
44+
45+ Y.detachAll('window-alt-S-pressed');
46+ Y.on('window-alt-S-pressed', function(data) {
47+ var field = Y.one('#charm-search-field');
48+ if (field) {
49+ field.focus();
50+ }
51+ data.preventDefault = true;
52+ }, this);
53+
54+ /**
55+ * It transforms a numeric keyCode value to its string version. Example:
56+ * 16 returns 'shift'.
57+ * @param {number} keyCode The numeric value of a key.
58+ * @return {string} The string version of the given keyCode.
59+ */
60+ function keyCodeToString(keyCode) {
61+ if (keyCode === 16) {
62+ return 'shift';
63+ }
64+ if (keyCode === 17) {
65+ return 'control';
66+ }
67+ if (keyCode === 18) {
68+ return 'alt';
69+ }
70+ if (keyCode === 27) {
71+ return 'esc';
72+ }
73+ // Numbers or Letters
74+ if (keyCode >= 48 && keyCode <= 57 || //Numbers
75+ keyCode >= 65 && keyCode <= 90) { //Letters
76+ return String.fromCharCode(keyCode);
77+ }
78+ //F1 -> F12
79+ if (keyCode >= 112 && keyCode <= 123) {
80+ return 'F' + (keyCode - 111);
81+ }
82+ return null;
83+ }
84+ },
85+
86+ /**
87 * @method initializer
88 */
89 initializer: function() {
90@@ -472,7 +551,6 @@
91 render: true,
92 callback: function(view) {view.postRender();}});
93 }
94- next();
95 },
96
97 /**
98
99=== modified file 'app/index.html'
100--- app/index.html 2012-11-19 18:49:43 +0000
101+++ app/index.html 2012-11-20 14:53:22 +0000
102@@ -74,6 +74,9 @@
103 <script>
104 YUI(GlobalConfig).use(["juju-gui"], function(Y) {
105 app = new Y.juju.App(juju_config);
106+ // We need to activate the hotkeys when running the application
107+ // in production. Unit tests should call it manually.
108+ app.activateHotkeys();
109 });
110 </script>
111 </body>
112
113=== modified file 'test/index.html'
114--- test/index.html 2012-11-19 19:52:43 +0000
115+++ test/index.html 2012-11-20 14:53:22 +0000
116@@ -33,6 +33,7 @@
117 <script src="test_endpoints.js"></script>
118 <script src="test_application_notifications.js"></script>
119 <script src="test_charm_store.js"></script>
120+ <script src="test_app_hotkeys.js"></script>
121 <script src="test_notifier_widget.js"></script>
122
123 <script>
124
125=== added file 'test/test_app_hotkeys.js'
126--- test/test_app_hotkeys.js 1970-01-01 00:00:00 +0000
127+++ test/test_app_hotkeys.js 2012-11-20 14:53:22 +0000
128@@ -0,0 +1,63 @@
129+'use strict';
130+
131+describe('application hotkeys', function() {
132+ var Y, app, container, env, conn, testUtils, windowNode;
133+
134+ before(function() {
135+ Y = YUI(GlobalConfig).use(
136+ ['juju-gui', 'juju-tests-utils',
137+ 'node-event-simulate'], function(Y) {
138+ windowNode = Y.one(window);
139+ app = new Y.juju.App({
140+ env: env,
141+ container: container,
142+ viewContainer: container
143+ });
144+ app.activateHotkeys();
145+ });
146+ });
147+
148+ afterEach(function() {
149+ container.remove(true);
150+ });
151+
152+ beforeEach(function() {
153+ container = Y.one('#main').appendChild(Y.Node.create('<div/>')).set('id',
154+ 'test-container').append(
155+ Y.Node.create('<input />').set('id', 'charm-search-field'));
156+ testUtils = Y.namespace('juju-tests.utils');
157+ env = {
158+ get: function() {
159+ },
160+ on: function() {
161+ },
162+ after: function() {
163+ }
164+ };
165+ });
166+
167+ it('should listen for alt-S events', function() {
168+ app.render();
169+ windowNode.simulate('keydown', {
170+ keyCode: 83, // "S" key
171+ altKey: true
172+ });
173+ // Did charm-search-field get the focus?
174+ assert.equal(Y.one('#charm-search-field'), Y.one(document.activeElement));
175+ });
176+
177+ it('should listen for alt-E events', function() {
178+ var altEtriggered = false;
179+ app.on('navigateTo', function(param) {
180+ if( param && param.url === '/' ) {
181+ altEtriggered = true;
182+ }
183+ });
184+ app.render();
185+ windowNode.simulate('keydown', {
186+ keyCode: 69, // "E" key
187+ altKey: true
188+ });
189+ assert.isTrue(altEtriggered);
190+ });
191+});

Subscribers

People subscribed via source and target branches