Make improvements to RB.DialogView
Review Request #10820 — Created Jan. 13, 2020 and submitted
This review request changes makes improvements to
RB.DialogView.
It introduces a property on
RB.DialogView
which is a map of buttons in the dialog from buttonid
s to
button elements. Theid
field of aRB.DialogView
button is now required so that
buttons can be referenced by their id in the map. In addition,disabled
andclass
are added as button keys so that buttons can be created with initial state set to disabled
and with classes set, respectively.
Unit tests:
- new dialog view button properties (id, class, disabled)
Description | From | Last Updated |
---|---|---|
In your description, we don't need to talk about browsers without JS--Review Board really isn't usable if JS is turned … |
david | |
Thanks for working on this, Hannah! Reversing the order of buttons may fix it, but I think only accidentally (and … |
chipx86 | |
Just curious, can you test what happens when esc is pressed? |
david | |
Oh, one last thing. May have mentioned this before, but we now effectively have two distinct changes tangled up here: … |
chipx86 | |
We should call this only if there's an id set. I'd make this its own statement, wrapped in an if … |
chipx86 | |
Rather than define these, let's use the definitions that already exist: $.ui.keyCode.ENTER and $.ui.keyCode.ESCAPE. |
chipx86 | |
Let's pull buttons[0] into a variable and then test against that. It'll be a little faster (avoids repeated index lookups). |
chipx86 | |
Jasmine has a handy .toBeFalse() and .toBeTrue(), which is more correct in these cases. |
chipx86 | |
You'll want to do this right at the class level, like: events: _.extend({ 'change #path': '.....', }, RB.DialogView.prototype.events), |
chipx86 | |
So something that I noticed when I looked at the original code we had earlier is that we're playing with … |
chipx86 | |
This is still best done in render. It's kind of like initialize but for the initial display, so it's the … |
chipx86 | |
Is there a reason why we're using !!buttonInfo.danger instead of just simply buttonInfo.danger? |
bui1 | |
Col: 37 ['testid'] is better written in dot notation. |
reviewbot | |
Col: 37 ['testid'] is better written in dot notation. |
reviewbot | |
Col: 37 ['testid'] is better written in dot notation. |
reviewbot | |
Col: 37 ['testid'] is better written in dot notation. |
reviewbot | |
Col: 41 ['testid'] is better written in dot notation. |
reviewbot | |
Col: 41 ['testid'] is better written in dot notation. |
reviewbot | |
Col: 41 ['upload'] is better written in dot notation. |
reviewbot | |
We like to keep these in alphabetical order, but have completely failed to do so here. Doh. Can you shuffle … |
chipx86 | |
"ID" should be capitalized in sentences. |
chipx86 | |
I think we only want to do this inside of the code building the body and modalBox. Otherwise, a second … |
chipx86 | |
I think this should probably go right before modalBox(), so we're not at risk of page elements jumping around or … |
chipx86 | |
So the biggest thing I'm noticing is that we're now returning a mapping exclusively. The issue is that we're no … |
chipx86 | |
We can just set this to true directly, since we already know that's the value. |
chipx86 | |
toggleClass isn't very useful here, since we already know we want to enable these classes. It's actually a bit slower, … |
chipx86 | |
I mentioned this in a reply to Monica's review, but we don't need to use reduce here. Even though there's … |
chipx86 | |
I think this can now be this.$('.js-path') (and you could remove the id="path" on the element). If we can remove … |
chipx86 |
- Change Summary:
-
Fix description.
- Description:
-
~ Fix bug where pressing enter on upload file dialog would cancel the upload instead of actually uploading it. This bug occurred because the first button, which was previously the cancel button in the dialog, is always the default button used to submit a form. The fix is to swap the ordering of the cancel and upload buttons so that the upload button comes first. (It can be fixed without changing the UI by using JS but this can be confusing from an accessibility perspective and can be problematic in browsers without scripting.)
~ Fix bug where pressing enter on upload file dialog would cancel the upload instead of actually uploading it. This bug occurred because the first button, which was previously the cancel button in the dialog, is always the default button used to submit a form. The fix is to swap the ordering of the cancel and upload buttons so that the upload button comes first. (It can be fixed without changing the UI by using JS but this can be confusing from an accessibility perspective.)
-
-
Thanks for working on this, Hannah!
Reversing the order of buttons may fix it, but I think only accidentally (and I don't like having to shuffle around UI to make something work). Since the Upload button is disabled by default, I don't believe we're guaranteed that all browsers will treat it as the primary button.
It's always better to be explicit in how a bug gets fixed, particularly when we're dependent on browser behavior. Also, we're going to encounter this in other dialogs as well, which may have even more buttons and more complicated button layouts.
modalBox is ooooold
So this is old code. We actually have this far-nicer thing called
RB.DialogView
, which we'd like to port code to. It has a concept of primary buttons, too.It's a bit newer, and it might need some improvement, but that's where any work toward doing this correctly should go.
So I'd suggest changing this code to not call
.modalBox()
directly (okay,RB.DialogView
does call this, but don't worry about that). You can create the element for the contents of the dialog and instantiate using:const dialogView = new RB.DialogView({ title: ..., body: yourElement, buttons: [ { label: gettext('Upload'), primary: true, onClick: () => this.send(), }, { label: gettext('Cancel'), } ], });
Something like that.
Now, this probably won't fix it directly, but here's where phase 2 comes in! You can modify
reviewboard/static/rb/js/ui/views/dialogView.es6.js
to register a key event for Enter and a key event for Escape and hook those up, like:RB.DialogView = Backbone.View.extend({ events: { 'keypress': _onKeyPress, }, ... _onKeyPress(e) { /* Check for Enter and Escape here */ } });
If Enter, activate the primary button if we have one (loop through
this.options
, look forprimary
).If Escape, just call
this.hide()
.I think this would be fantastic addition, and would solve the bug cleanly!
Make sure that you write unit tests, though. Check out
ui/views/tests/dialogView.es6.js
. You'll want to simulate key events. Here's how:const evt = $.Event('keypress') evt.which = $.ui.keyCode.ENTER; dialogView.$el.trigger(evt);
- Change Summary:
-
Fix issues from review and start writing tests
- Summary:
-
Fix for pressing enter on upload file dialog cancelling upload[WIP] Fix for pressing enter on upload file dialog cancelling upload
- Commit:
-
28b47004ab97e9a341cc27f7593594d8fe5589d1df06de041ae1009e3815d5448d2b7a300e680680
Checks run (2 succeeded)
-
-
We should call this only if there's an
id
set. I'd make this its own statement, wrapped in anif
that checks for it.(Frankly, we should probably be doing that with all the things we're adding -- we're cheating a bit with the
toggleClass
thing, and it's not as performant as just anif
withaddClass
.) -
Rather than define these, let's use the definitions that already exist:
$.ui.keyCode.ENTER
and$.ui.keyCode.ESCAPE
. -
Let's pull
buttons[0]
into a variable and then test against that. It'll be a little faster (avoids repeated index lookups). -
-
You'll want to do this right at the class level, like:
events: _.extend({ 'change #path': '.....', }, RB.DialogView.prototype.events),
-
So something that I noticed when I looked at the original code we had earlier is that we're playing with fire using these IDs.
#upload
and#path
are wayyyyy too generic, and are bound to conflict. There should never be more than one element with an ID, and if there is, bad things can happen with event handling and such.So what I'd suggest doing is two things:
- Adding support for CSS classes instead of or in addition to IDs, when registering buttons.
- Using our CSS standard naming scheme to come up with suitable classes.
For the latter, I don't want to have you pour too much more work into this, because this can get out of control, so instead of revamping the entire CSS for this dialog, we can make use of these handy
js-
-prefixed classes. So,js-path
andjs-upload-button
.What
js-
means is "I'm a thing owned by some JavaScript, and nothing but the thing that owns me should deal with me." So no custom styling, so page-wide DOM queries, nothing. It's good for this purpose.By using those instead of hard-coded IDs, we'll avoid potentially weird bugs down the line.
-
This is still best done in
render
. It's kind of likeinitialize
but for the initial display, so it's the right place to pull out variables. We then don't have to defineshow()
, letting us just take advantage of the parent class's version, and trusting that we'll be pulling out variables before it's actually shown.Make sure though, especially with the above
js-
prefixes I mentioned, to isolate the lookup to this element. You can use a shorthand available to views:this._$path = this.$('.js-path'); this._$uploadBtn = this.$('.js-upload-button');
- Change Summary:
-
Fixed more issues from review and finished writing tests
- Summary:
-
[WIP] Fix for pressing enter on upload file dialog cancelling uploadFix for pressing enter on upload file dialog cancelling upload
- Description:
-
~ Fix bug where pressing enter on upload file dialog would cancel the upload instead of actually uploading it. This bug occurred because the first button, which was previously the cancel button in the dialog, is always the default button used to submit a form. The fix is to swap the ordering of the cancel and upload buttons so that the upload button comes first. (It can be fixed without changing the UI by using JS but this can be confusing from an accessibility perspective.)
~ Fix bug where pressing enter on upload file dialog would cancel the upload
+ instead of actually uploading it. This bug occurred because the first button, + which is the cancel button in the dialog, is always the default button used + to submit a form. The fix is to make the upload button the primary, or + default, button and use the Enter key event to upload the file if one has + been uploaded. This RB changes uploadAttachmentView
to extenddialogView
+ as opposed to calling the dialogView
constructor directly. In addition, it+ adds a handler for the Escape key so that the dialog can be closed using the + Escape key. - Testing Done:
-
~ Tested uploading a file via the dialog and using the enter key to upload. Also tested that uploading still works by pressing the "Upload" button and that you can cancel by pressing the "Cancel" button.
~ Manually tested:
+ - uploading a file via the dialog and using the enter key to upload or by + pressing the "Upload" button + - canceling the dialog by pressing the escape key or by pressing the "Cancel" + button + + Unit tests:
+ - new dialog view button properties (id, class, disabled) + - pressing the enter key when the primary button is enabled and disabled and + pressing the escape key for the dialog - Commit:
-
df06de041ae1009e3815d5448d2b7a300e680680e2049101c0a9150946dc251e344d4e8c5b0cbcb6
Checks run (2 succeeded)
- Change Summary:
-
Make selectors more specific
- Commit:
-
e2049101c0a9150946dc251e344d4e8c5b0cbcb6f973c8515471201d371fe32cf2ca34a36b13dd4e
Checks run (2 succeeded)
-
-
-
Just a question out of curiousity but what will happen to the dialogue if neither enter/esc is pressed on the keyboard but rather another keyCode like any of the digits/alphabet etc?
Will the dialogue stay open and not produce any errors? Does the dialogue just ignore those key inputs?
-
Also wondering why 'send' is used for onClick instead of a function that would trigger an event that would allow the user to upload their file?
- Change Summary:
-
Remove bug fix and make this review request just do the refactoring for RB.UploadAttachmentView - bug fix to come in another rb
- Summary:
-
Fix for pressing enter on upload file dialog cancelling uploadRefactor RB.UploadAttachmentView to extend RB.DialogView
- Description:
-
~ Fix bug where pressing enter on upload file dialog would cancel the upload
~ instead of actually uploading it. This bug occurred because the first button, ~ which is the cancel button in the dialog, is always the default button used ~ to submit a form. The fix is to make the upload button the primary, or ~ default, button and use the Enter key event to upload the file if one has ~ been uploaded. This RB changes uploadAttachmentView
to extenddialogView
~ as opposed to calling the dialogView
constructor directly. In addition, it~ This review request changes
RB.UploadAttachmentView
to extendRB.DialogView
as opposed~ to calling the RB.DialogView
constructor directly. It also introduces a property on~ RB.DialogView
which is a map of buttons in the dialog from buttonid
s to~ button elements. The id
field of aRB.DialogView
button is now required so that~ buttons can be referenced by their id in the map. In addition, disabled
andclass
~ are added as button keys so that buttons can be created with initial state set to disabled ~ and with classes set, respectively. - adds a handler for the Escape key so that the dialog can be closed using the - Escape key. - Testing Done:
-
Manually tested:
~ - uploading a file via the dialog and using the enter key to upload or by ~ pressing the "Upload" button ~ - uploading a file via the dialog pressing the "Upload" button ~ - canceling the dialog by pressing the "Cancel" button - - canceling the dialog by pressing the escape key or by pressing the "Cancel" - button Unit tests:
~ - new dialog view button properties (id, class, disabled) ~ - new dialog view button properties (id, class, disabled) - - pressing the enter key when the primary button is enabled and disabled and - pressing the escape key for the dialog - Commit:
-
f973c8515471201d371fe32cf2ca34a36b13dd4ed8213875ed85f4cae1ed1fba87d52a9c99a7e582
- Diff:
-
Revision 5 (+139 -69)
Checks run (1 failed, 1 succeeded)
JSHint
- Change Summary:
-
Fix JSHint errors
- Commit:
-
d8213875ed85f4cae1ed1fba87d52a9c99a7e582a85ce6c06e828f08cfd325f2049e5ededc40c963
- Diff:
-
Revision 6 (+139 -69)
Checks run (2 succeeded)
-
Changes look really good! Only have one small comment that I'm not too sure about
-
I really like how simple it is to read the reduce function callback.
More for a question for any mentor/maintainer reviewing this, are reduce functions allowed for es6 files? I've looked through https://www.notion.so/reviewboard/JavaScript-ES6-Guidelines-a5d6efb850d24ac295e1b8792cecdd25
but couldn't find anything on it
-
-
We like to keep these in alphabetical order, but have completely failed to do so here. Doh.
Can you shuffle these around so they're in order?
-
-
I think we only want to do this inside of the code building the body and
modalBox
. Otherwise, a second call would replace our button map. -
I think this should probably go right before
modalBox()
, so we're not at risk of page elements jumping around ormodalBox
's size calculations being incorrect as things re-render. -
So the biggest thing I'm noticing is that we're now returning a mapping exclusively. The issue is that we're no longer going to have ordering information preserved, so we're browser-dependent. This could cause buttons to swap around.
We're going to want to ultimately end up having two pieces of information calculated:
- A list of button elements (to pass to
modalBox
) -- let's call thisthis.$buttonList
. - A mapping of button elements, for
render()
to reference. -- let's call thisthis.$buttonsMap
, for consistency.
How about we rename this to
_makeButtons()
, and we just populate both of those attributes instead of returning anything? Then it behaves the way thatshow()
now uses it, and it gives us public attributes for both of these useful bits of state.It'll be important to start the function off by clearing both of those attributes before we do anything.
- A list of button elements (to pass to
-
-
toggleClass
isn't very useful here, since we already know we want to enable these classes. It's actually a bit slower, since it's first going to split the existing class list from a string and do a search in there. So instead, let's just useaddClass()
with the class name we want. -
I mentioned this in a reply to Monica's review, but we don't need to use
reduce
here. Even though there's so few buttons in practice, we want to avoid a second loop (and all the function calls that come from each iteration of areduce
call).Instead, let's just set the entry in the map up above, after we've set all the button state (so after the
if (buttonInfo.onClick) { ... }
block).
- Change Summary:
-
Fix issues from review
- Commit:
-
a85ce6c06e828f08cfd325f2049e5ededc40c963a32ab1a31ed5e84bdd00a853caa7cf0ee24b7707
- Diff:
-
Revision 7 (+156 -85)
Checks run (2 succeeded)
-
This looks just about done! One tiny thing (and you can also close the opened issue -- you got it right!).
-
I think this can now be
this.$('.js-path')
(and you could remove theid="path"
on the element). If we can remove the IDs on the elements in this dialog, we'll reduce the risk of hard-to-diagnose conflicts.
-
-
Oh, one last thing. May have mentioned this before, but we now effectively have two distinct changes tangled up here:
- A set of improvements to
RB.DialogView
, which are independent of the changes toRB.UploadAttachmentView
. - The fixes to
RB.UploadAttachmentView
and making it a subclass ofRB.DialogView
.
So I'd suggest repurposing this change for the
RB.DialogView
cahnges and describing them, and then having a follow-up review request just for theUploadAttachmentView
changes.It's helpful for a variety of reasons to keep each review request/commit focused like that, so we encourage it where possible.
- A set of improvements to
- Change Summary:
-
Remove path id
- Commit:
-
a32ab1a31ed5e84bdd00a853caa7cf0ee24b7707ca287ea7e0daa0eaf1255516435d40407685b6ec
- Diff:
-
Revision 8 (+156 -85)
Checks run (2 succeeded)
- Change Summary:
-
Remove fixes for UploadAttachmentView - to be moved to another review request.
- Summary:
-
Refactor RB.UploadAttachmentView to extend RB.DialogViewMake improvements to RB.DialogView
- Description:
-
~ This review request changes
RB.UploadAttachmentView
to extendRB.DialogView
as opposed~ This review request changes makes improvements to
RB.DialogView.
It introduces a property on- to calling the RB.DialogView
constructor directly. It also introduces a property onRB.DialogView
which is a map of buttons in the dialog from buttonid
s tobutton elements. The id
field of aRB.DialogView
button is now required so thatbuttons can be referenced by their id in the map. In addition, disabled
andclass
are added as button keys so that buttons can be created with initial state set to disabled and with classes set, respectively. - Testing Done:
-
- Manually tested:
- - uploading a file via the dialog pressing the "Upload" button - - canceling the dialog by pressing the "Cancel" button - Unit tests:
- new dialog view button properties (id, class, disabled) - Bugs:
-
- Commit:
ca287ea7e0daa0eaf1255516435d40407685b6ec2ce290531cacd249caa26e12883850637b5b0191- Diff:
Revision 9 (+122 -48)
Checks run (2 succeeded)
flake8 passed.JSHint passed.