Add confirmation dialog for post-commit review requests on web UI

Review Request #10148 — Created Sept. 17, 2018 and submitted

Information

Review Board
release-2.5.x
9efe7bf...

Reviewers

When a user is scrolling through the changeset (post commit review) to
make a new review request on the web UI, clicking on the row of a
commit makes a new review request, which can be done accidentally very
easily.

I added a confirmation dialog when creating post-commit review requests
on web UI so the user can confirm that they want to create a new review
request from that commit. Clicking 'cancel' closes the dialog and takes
no further action, clicking 'create' creates a new review request.

Ran JS tests (all of which pass).

Tested behaviour in Chrome, Safari, and Firefox with the following
scenarios:

  1. Click on commit, click cancel on confirmation dialog
  2. Click on commit with no diff with master, click confirm, see error
    message
  3. Click on commit with diff with master, click confirm, see review
    request created

Description From Last Updated

Can you wrap your summary at 72 characters, but ideally 52 characters. Commit summaries needn't end in a period.

brenniebrennie

You can have your testing done render a list using: Tested behaviour in Chrome, Safari in Firefox: 1. Something 2. …

brenniebrennie

The issue was originally filed against reviewboard 2.5.x. Can you apply your patch on that branch, test, and re-post? Note: …

brenniebrennie

Could you add a screen shot of your work?

brenniebrennie

Can you wrap your description at 72 characters?

brenniebrennie

One more request to add to the design of this: can we include the commit summary in the text of …

daviddavid

Tiny nit: summaries don't need a period.

brenniebrennie

Also your summary should be in the imperitive mood as if it were an order. If you substitute your sumamry …

brenniebrennie

Ideally this long message would be in the body of the dialog instead of the title

brenniebrennie

How about: "The confirmation dialog will allow the user to cancel the creation of the review request."

brenniebrennie

We may want to add more context to the message here, something like: You are creating a new review request …

brenniebrennie

This string needs to be marked for translation with gettext().

brenniebrennie

This string needs to be marked for translation with gettext(). Missing trailing comma.

brenniebrennie

This string needs to be marked for translation with gettext().

brenniebrennie

Unfortunately, we can't use ES6 arrow functions here. We'll have to do _.bind(this._createNewReviewRequest, this) instead. This is because we have …

brenniebrennie

Instead of doing this, lets define a subclass that will display this content. We can refactor it out into a …

brenniebrennie

Can you add body and title here? The title should be something like "Are you sure?" or "Create review request?"

brenniebrennie

This should really be in the default impl of render above. Also, doing _.result twice may result in us calling …

brenniebrennie

Title case: Create Review Request?

brenniebrennie

While you're here, would you mind updating the doc comment to take the following format: /** * Initialze the view. …

brenniebrennie

Need a var body statement at the top of the function.

brenniebrennie

if (body) {

brenniebrennie

Instead of duplicating this I would just do See the :js:class:`class-level documentation <RB.DialogView>` for the keys that this object accepts.

brenniebrennie

Let's sort these alphabetically.

daviddavid

Please put a blank line between these two.

daviddavid

Instead of <b>, let's just use <span> and then put font-weight: bold in the CSS.

daviddavid

Because this is an ES5 file, no trailing comma on the last item in an object.

daviddavid

Because this is an ES5 file, no trailing comma on the last item in an object.

daviddavid

A few things here. Wrapping everything in gettext wont work. gettext() requires the value to a be a string literal, …

brenniebrennie

While you're here, can you change this to /**? Otherwise it doesn't get picked up by the documentation generation tool

brenniebrennie

And this?

brenniebrennie

This is only used once, so instead of defining a variable, you could do it inline: body: this._dialogBodyTemplate({ prefixText: gettext('...'), …

daviddavid

In ES5, all variables need to be defined at the top of the function. So: var commitID = this.model.get('id'), dialogView; …

daviddavid

Wrap to 79 columns.

daviddavid
shoven
shoven
shoven
brennie
  1. 
      
  2. Show all issues

    Can you wrap your summary at 72 characters, but ideally 52 characters. Commit summaries needn't end in a period.

  3. Show all issues

    You can have your testing done render a list using:

    Tested behaviour in Chrome, Safari in Firefox:
    
    1. Something
    2. Another thing
    3. Yet another thing.
    
  4. Show all issues

    The issue was originally filed against reviewboard 2.5.x.

    Can you apply your patch on that branch, test, and re-post?

    Note:

    To get Reviewboard 2.5.x to work, you will need a new virtual environment with Review Board at release-2.5.x and Djblets at release-0.9.x. You do not need another source checkout of RB or Djblets -- another virtualenv will suffice.

  5. Show all issues

    Could you add a screen shot of your work?

  6. Show all issues

    Can you wrap your description at 72 characters?

    1. You wrapped your description and testing done at 52, which just the 1-line summary needs to be wrapped at.

  7. Show all issues

    How about:

    "The confirmation dialog will allow the user to cancel the creation of the review request."

  8. Show all issues

    We may want to add more context to the message here, something like:

    You are creating a new review request from a commit that has already been published. Are you sure you want to continue?
    
  9. Show all issues

    This string needs to be marked for translation with gettext().

  10. Show all issues

    This string needs to be marked for translation with gettext().

    Missing trailing comma.

  11. Show all issues

    This string needs to be marked for translation with gettext().

  12. Show all issues

    Unfortunately, we can't use ES6 arrow functions here. We'll have to do _.bind(this._createNewReviewRequest, this) instead.

    This is because we have customers on older versions of IE (I know :( ). We do allow arrow functions in .es6.js files, which are transpiled with Babel to ES5 that can run on all these browsers.

    However, since this patch will be backported to 2.5.x, we don't be able to use that. Sorry.

  13. 
      
shoven
shoven
shoven
brennie
  1. 
      
  2. Show all issues

    Ideally this long message would be in the body of the dialog instead of the title

    1. I tried one approach by adding a 'body' option to the dialogView class to make this change. I'm not sure if this is what you were expecting based on our conversation in Slack, but continuing to use the dialogView class allowed me to take advantage of the existing functionality to set the button list correctly for modalBox. I added a new screenshot of what it looks like now. Let me know what you think.

  3. Show all issues

    Instead of doing this, lets define a subclass that will display this content. We can refactor it out into a generic class at a later date.

    If you wrap the contents of the file in an IIFE, we can have it local to the file.

    1. Can you elaborate on this a bit? I'm not sure I understand exactly what you mean here...

    2. You can ignore this. Your approach is better.

  4. 
      
shoven
brennie
  1. 
      
  2. Show all issues

    Can you add body and title here?

    The title should be something like "Are you sure?" or "Create review request?"

  3. reviewboard/static/rb/js/ui/views/dialogView.js (Diff revision 3)
     
     
     
     
    Show all issues

    This should really be in the default impl of render above. Also, doing _.result twice may result in us calling an expensive function twice. Lets do:

    var body;
    
    /* snip */
    
    body = _.result(this, 'body');
    
    if (body !== undefined) {
        this.$el.append($('<p>').text(body));
    }
    
  4. 
      
shoven
shoven
brennie
  1. 
      
  2. Show all issues

    Title case: Create Review Request?

  3. reviewboard/static/rb/js/ui/views/dialogView.js (Diff revision 4)
     
     
     
     
     
     
     
     
     
     
     
     
    Show all issues

    While you're here, would you mind updating the doc comment to take the following format:

    /**
     * Initialze the view.
     * 
     * Args:
     *     options (object):
     *         Options for the view.
     *
     * Option Args:
     *     body (string or function, optional):
     *         <Description of body.>
     *         
     *     title (string, optional):
     *         <Description of title.>
     *
     *     buttons (object, optional):
     *         <Description of buttons.>
     */
    
    1. If you can take a look at what I did, do you think I need to put the parameters of the buttons here like I did even though it's duplicating the description at the top of the file?

  4. Show all issues

    Need a var body statement at the top of the function.

  5. Show all issues

    if (body) {
    
  6. 
      
david
  1. 
      
  2. Show all issues

    One more request to add to the design of this: can we include the commit summary in the text of the dialog, so people can verify that they clicked on the one they intended to?

    1. I added a screenshot of my changes. I also added the first few characters of the commit id like in the commit view in the list. Let me know what you think.

  3. 
      
shoven
shoven
brennie
  1. 
      
  2. reviewboard/static/rb/js/ui/views/dialogView.js (Diff revision 6)
     
     
     
     
     
     
     
     
     
     
     
     
    Show all issues

    Instead of duplicating this I would just do

    See the :js:class:`class-level documentation <RB.DialogView>` for the keys that this object accepts.
    
  3. 
      
shoven
david
  1. Just a few tiny nits left.

  2. reviewboard/static/rb/css/common.less (Diff revision 7)
     
     
     
    Show all issues

    Let's sort these alphabetically.

  3. Show all issues

    Please put a blank line between these two.

  4. Show all issues

    Instead of <b>, let's just use <span> and then put font-weight: bold in the CSS.

  5. Show all issues

    Because this is an ES5 file, no trailing comma on the last item in an object.

    1. In the first review Barret gave me he said I was missing trailing commas so I added them everywhere. So I shouldn't add them unless it's an es6.js file?

    2. That was my bad. I forgot trailing commas in objects aren't an ES5 thing. Sorry 😦

  6. Show all issues

    Because this is an ES5 file, no trailing comma on the last item in an object.

    1. Same thing, in the first review Barret gave me he said I was missing trailing commas so I added them everywhere. So I shouldn't add them unless it's an es6.js file?

  7. 
      
shoven
brennie
  1. 
      
  2. Show all issues

    A few things here. Wrapping everything in gettext wont work. gettext() requires the value to a be a string literal, otherwise the code that looks for things to translate won't be able to parse this.

    Also, we want to avoid using inline styles when possible. This should be included in the rules for .summary in the LESS file.

    Instead, lets use a template on the class:

    {
        _dialogTemplate: _.template([
            '<p><%- prefixText %></p>',
            '<p class="summary">',
            ' <%- commitID %>: <%- summary %>',
            '</p>',
            '<p><%- suffixText %></p>',
        ].join('')),
    }
    

    For more details on _.template, check out the docs.

    Then we can use it like so:

    var dialogView = new RB.DialogView({
        title: gettext('Create Review Request?'),
        body: this._dialogTemplate({
            prefixText: gettext('You are creating a new review request from the following published commit:'),
            suffixText: gettext('Are you sure you want to continue?'),
        }), 
        buttons: [ /* ... */ ]
    });
    

    This allows us to only mark the string literals for translation. Additionally, lets use <p> tags for content separation instead of <br>, as that is what they exist for.

  3. 
      
shoven
brennie
  1. Two minor nits, but otherwise looks good to me!

  2. Show all issues

    While you're here, can you change this to /**? Otherwise it doesn't get picked up by the documentation generation tool

    1. I changed this for all the functions I touched. Should I fix it for the rest or leave them for now (i.e. it should be in a different patch)

    2. Just the ones you touched are fine. Cleanup can be a different patch.

  3. Show all issues

    And this?

  4. 
      
brennie
  1. 
      
  2. Show all issues

    Tiny nit: summaries don't need a period.

  3. Show all issues

    Also your summary should be in the imperitive mood as if it were an order. If you substitute your sumamry into the following sentence it should make sense:

    This patch will <summary>
    

    So Add confirmation dialog ... would work or similar.

  4. 
      
shoven
david
  1. 
      
  2. Show all issues

    This is only used once, so instead of defining a variable, you could do it inline:

    body: this._dialogBodyTemplate({
        prefixText: gettext('...'),
        commitID: commitID,
        summary: this.model.get('summary'),
        suffixText: ...
    }),
    
  3. Show all issues

    In ES5, all variables need to be defined at the top of the function. So:

    var commitID = this.model.get('id'),
        dialogView;
    
    if (commitID.length > 7) {
        commitID = commitID.slice(0, 7);
    }
    
    dialogView = new RB.DialogView({
        ...
    
  4. Show all issues

    Wrap to 79 columns.

  5. 
      
shoven
brennie
  1. Ship It!
  2. 
      
david
  1. Ship It!
  2. 
      
shoven
Review request changed
Status:
Completed
Change Summary:
Pushed to release-3.0.x (7746104)