Merge lp:~tveronezi/juju-gui/hotkeys into lp:juju-gui/experimental
- hotkeys
- Merge into trunk
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 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Benjamin Saller (community) | Needs Fixing | ||
Review via email: mp+131382@code.launchpad.net |
Commit message
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.
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(
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://
>
> such that we can say things like
>
> // require modifier keys with +(modifier)
> Y.one('
>
> 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:/
> You are the owner of lp:~tveronezi/juju-gui/hotkeys.
>
- 210. By Thiago Veronezi
-
trunk merge
- 211. By Thiago Veronezi
-
trunk merge
- 212. By Thiago Veronezi
-
personal code review
Thiago Veronezi (tveronezi) wrote : | # |
Please take a look.
Thiago Veronezi (tveronezi) wrote : | # |
Reviewers: mp+131382_
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:/
(do not edit description out of merge proposal)
Please review this at https:/
Affected files:
A [revision details]
M app/app.js
M app/index.html
M test/index.html
A test/test_
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:/
File app/app.js (right):
https:/
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:/
app/app.js:145: this.show_
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://
* 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:/
app/app.js:557: if (next) {
Perhaps Y.Lang.
Thiago Veronezi (tveronezi) wrote : | # |
Thanks for your review, Matt!
https:/
File app/app.js (right):
https:/
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:/
app/app.js:145: this.show_
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://
> 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_
https:/
app/app.js:557: if (next) {
On 2012/11/19 18:36:51, matthew.scott wrote:
> Perhaps Y.Lang.
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?
Madison Scott-Clary (makyo) wrote : | # |
Thanks for the reply! +1 with the navigateTo change
https:/
File app/app.js (right):
https:/
app/app.js:145: this.show_
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://
> > 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_
});
Plugged this in, works well. Thanks!
https:/
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.
> 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.
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:/
File app/app.js (right):
https:/
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.
> 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.
- 213. By Thiago Veronezi
-
code review
Thiago Veronezi (tveronezi) wrote : | # |
Thanks for your review Ben!
[]s,
Thiago.
https:/
File app/app.js (right):
https:/
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.
> >
> > 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.
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:/
Preview Diff
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 | +}); |
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) doc').on( 'key', composeMail, 'n+ctrl');
Y.one('
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 :)