Enhance the review request blank state user experience

Review Request #10928 — Created Feb. 29, 2020 and updated — Latest diff uploaded

Information

Review Board
master

Reviewers

The review request page when the user creates a new draft to publish their changes
is empty at first unless some fields have been populated by RB Tools such as
the commit summary and description. However, not always these fields are populated
and the user is subjected to a mostly empty page. This can be a confusing user
experience where one may not know which fields are required to post a change,
what kind of content is needed for the different inputs, and where to add new files
to the request. These changes will help clarify the user experience on this page
to make it more friendly, and easier to understand what changes are needed to make
on this request before posting their work.

These changes include,
- The addition of a edit icon to the file attachment area and to make it render by
default on review request drafts
- Creating a file attachment box area where users can click a button or drag and
drop a file into, making it easier to figure out where/how to add files to a request
- Changing the banner to include a checkbox group that holds all the required fields
that need to be filled out before posting a change
- Disabling the publish button until all the required checkboxes have been checked off
- Randomizing of placeholder texts from the Python backend
- Rendering of placeholder texts on various configured review request fields so the
user can understand what kind of input they need to make into a field

Refer to the screenshot (version 2) for what it looks like so far.

Manual Tests
- Using rbt post, check if the review request page renders properly with the
placeholder text, checkbox banner group, and file attachment areas shown
- Using the new review request page, check the same above scenario
- Placeholder text is rendered with a gray font color if the page is editable
by the user and if there is no previously saved value
- If there is a previously saved value, it renders with the blank font color
- Checkbox group in the draft banner has all the required fields from the
review request page
- Publish button stays disabled until the user has checked off all the required
check boxes
- Publish button renders disabled at first even when the page first renders if the
review request is not public yet and if the request is being edited on first save
- Placeholder text is randomly being changed from the python backend
- Placeholder text is being rendered if there is no previously saved value
- File attachment area is shown be default if the request is editable
- File attachment area is responsive on mobile even after adding/deleting files
- File attachment area is hidden if the request is not editable and if no files
exist for the review request
- All fields are keyboard accessible

Unit Tests
- Publish button is disabled when all required checkboxes are not checked off
- Publish button is enabled when all required checkboxes are checked off or
there are no required fields in the page
- File attachment area is shown by default if the review request is editable
- File attachment area is hidden if there are no attachments and the review
request is non-editable
- Python backend returns the proper required fields from RB default settings
- Placeholder text is rendered if there is no previously saved text
- Placeholder text is not rendered if there is previously saved text

reviewboard/reviews/builtin_fields.py
Revision d6e02045cee17e3ef2ebb00987ccd80f473fd728 New Change
26 lines
27
                                        ReviewRequestDraft,
27
                                        ReviewRequestDraft,
28
                                        Screenshot)
28
                                        Screenshot)
29
from reviewboard.scmtools.models import Repository
29
from reviewboard.scmtools.models import Repository
30
from reviewboard.site.urlresolvers import local_site_reverse
30
from reviewboard.site.urlresolvers import local_site_reverse
31

    
   
31

   

    
   
32
# File where the built in fields are created
32

    
   
33

   
33
logger = logging.getLogger(__name__)
34
logger = logging.getLogger(__name__)
34

    
   
35

   
35

    
   

   
36
class BuiltinFieldMixin(object):
36
class BuiltinFieldMixin(object):
37
    """Mixin for built-in fields.
37
    """Mixin for built-in fields.
38

    
   
38

   
39
    This overrides some functions to work with native fields on a
39
    This overrides some functions to work with native fields on a
40
    ReviewRequest or ReviewRequestDraft, rather than working with those
40
    ReviewRequest or ReviewRequestDraft, rather than working with those
326 lines
class SummaryField(BuiltinFieldMixin, BaseEditableField):
367

    
   
367

   
368
    field_id = 'summary'
368
    field_id = 'summary'
369
    label = _('Summary')
369
    label = _('Summary')
370
    is_required = True
370
    is_required = True
371
    tag_name = 'h1'
371
    tag_name = 'h1'

    
   
372
    placeholder_text = 'Test Summary Placeholder'
372

    
   
373

   
373
    #: The class name for the JavaScript view representing this field.
374
    #: The class name for the JavaScript view representing this field.
374
    js_view_class = 'RB.ReviewRequestFields.SummaryFieldView'
375
    js_view_class = 'RB.ReviewRequestFields.SummaryFieldView'
375

    
   
376

   
376

    
   
377

   
377
class DescriptionField(BuiltinTextAreaFieldMixin, BaseTextAreaField):
378
class DescriptionField(BuiltinTextAreaFieldMixin, BaseTextAreaField):
378
    """The Description field on a review request."""
379
    """The Description field on a review request."""
379

    
   
380

   
380
    field_id = 'description'
381
    field_id = 'description'
381
    label = _('Description')
382
    label = _('Description')
382
    is_required = True
383
    is_required = True
383

    
   
384

   
1

    
   
385
    # TODO use random module to randomize list of different placeholder texts to use

    
   
386
    placeholder_text = 'Testing placeholder text...'

    
   
387

   
384
    #: The class name for the JavaScript view representing this field.
388
    #: The class name for the JavaScript view representing this field.
385
    js_view_class = 'RB.ReviewRequestFields.DescriptionFieldView'
389
    js_view_class = 'RB.ReviewRequestFields.DescriptionFieldView'
386

    
   
390

   
387
    def is_text_markdown(self, value):
391
    def is_text_markdown(self, value):
388
        """Return whether the description uses Markdown.
392
        """Return whether the description uses Markdown.
8 lines
def is_text_markdown(self, value):
397
class TestingDoneField(BuiltinTextAreaFieldMixin, BaseTextAreaField):
401
class TestingDoneField(BuiltinTextAreaFieldMixin, BaseTextAreaField):
398
    """The Testing Done field on a review request."""
402
    """The Testing Done field on a review request."""
399

    
   
403

   
400
    field_id = 'testing_done'
404
    field_id = 'testing_done'
401
    label = _('Testing Done')
405
    label = _('Testing Done')

    
   
406
    placeholder_text = 'Placeholder dummy for testing done field...'
402

    
   
407

   
403
    #: The class name for the JavaScript view representing this field.
408
    #: The class name for the JavaScript view representing this field.
404
    js_view_class = 'RB.ReviewRequestFields.TestingDoneFieldView'
409
    js_view_class = 'RB.ReviewRequestFields.TestingDoneFieldView'
405

    
   
410

   
406
    def is_text_markdown(self, value):
411
    def is_text_markdown(self, value):
1174 lines
def _get_common_context(self, commits):
1581
        return {
1586
        return {
1582
            'include_author_name': include_author_name,
1587
            'include_author_name': include_author_name,
1583
            'to_expand': to_expand,
1588
            'to_expand': to_expand,
1584
        }
1589
        }
1585

    
   
1590

   
1586

    
   
1591
# field config list for the draft review request page
1587
class MainFieldSet(BaseReviewRequestFieldSet):
1592
class MainFieldSet(BaseReviewRequestFieldSet):
1588
    fieldset_id = 'main'
1593
    fieldset_id = 'main'
1589
    field_classes = [
1594
    field_classes = [
1590
        SummaryField,
1595
        SummaryField,
1591
        DescriptionField,
1596
        DescriptionField,
44 lines
class ChangeEntryOnlyFieldSet(BaseReviewRequestFieldSet):
1636
        FileAttachmentsField,
1641
        FileAttachmentsField,
1637
        ScreenshotsField,
1642
        ScreenshotsField,
1638
        StatusField,
1643
        StatusField,
1639
    ]
1644
    ]
1640

    
   
1645

   
1641

    
   
1646
# order in which the fields are rendered by review_request_box.js
1642
builtin_fieldsets = [
1647
builtin_fieldsets = [
1643
    MainFieldSet,
1648
    MainFieldSet,
1644
    ExtraFieldSet,
1649
    ExtraFieldSet,
1645
    InformationFieldSet,
1650
    InformationFieldSet,
1646
    ReviewersFieldSet,
1651
    ReviewersFieldSet,
1647
    ChangeEntryOnlyFieldSet,
1652
    ChangeEntryOnlyFieldSet,
1648
]
1653
]
reviewboard/reviews/fields.py
reviewboard/reviews/views.py
reviewboard/reviews/models/review_request_draft.py
reviewboard/reviews/templatetags/reviewtags.py
reviewboard/static/rb/css/pages/reviews.less
reviewboard/static/rb/css/ui/banners.less
reviewboard/static/rb/js/ui/views/inlineEditorView.es6.js
reviewboard/static/rb/js/ui/views/textEditorView.es6.js
reviewboard/static/rb/js/views/reviewRequestEditorView.es6.js
reviewboard/static/rb/js/views/reviewRequestFieldViews.es6.js
reviewboard/static/rb/js/views/tests/reviewRequestEditorViewTests.es6.js
reviewboard/templates/reviews/review_detail.html
reviewboard/templates/reviews/review_request_box.html
reviewboard/templates/reviews/review_request_box.js