Bulk editing of mappings #118

Merged
merged 66 commits into from Dec 20, 2013

Projects

None yet

4 participants

@jennyd
Member
jennyd commented Dec 18, 2013

Opening this to start more visible discussion, since it's a big one.

  • select multiple mappings from a site mappings index page
    • individually without javascript
    • using header checkbox for all or shift-click for ranges with javascript
  • choose to archive or redirect the selected mappings
  • see a pre-save confirmation page or modal, with a new URL input if redirecting
  • if all mappings are successfully updated, redirect to the last-seen site mappings index page
  • if the submitted new URL is invalid, (re)display the confirmation page with all mappings and the error message
  • if some mappings can't be updated for another reason (e.g. they are already invalid, probably because import bypasses validation), redisplay the confirmation page with only the ones which failed

The only acceptance criterion from the story which this doesn't address at all is client-side validation of the new URL input, but we think that is done adequately server-side for now.

fofr and others added some commits Dec 6, 2013
@fofr @jennyd fofr Indent mappings table header row b75cbd7
@fofr @jennyd fofr Create bulk editing table header styles
* Prepare table for form and checkboxes
* Switch add button from green to grey
* Add new table styles to style guide
47a6260
@fofr @jennyd fofr Create selectable and selected row styles
* Style labels and checkboxes to make entire cell clickable
* Add examples to style guide
* Stub actual pages with the expected HTML
* Move padding helper from hits to base
7800967
@fofr @jennyd fofr Select table rows with JavaScript
* Toggle row class and checkbox state on selection
* Toggle all when using the header checkbox
* Use indeterminate state in header when only some of the rows are
selected
* Update style guide example
e2afee4
@fofr @jennyd fofr Select groups of rows using shift click
* Use the previously interacted with checkbox and the newly clicked
checkbox to determine the range
* Currently only toggles on
9d10643
@jennyd jennyd Add form around table posting to edit_multiple mappings 9bd72eb
@jennyd jennyd Don't select all mappings when submitting the form
Specify js interaction using class name so that only clicking the
header checkbox toggles table rows.
b22ea14
@fofr fofr Handle shift click ranges correctly
* Shift click above and below previously selected rows
* Shift clicking now toggles the range based on the state of the
clicked row, eg. you can now unselect in groups
03899c0
@fofr fofr Mark rows as selected on load
* The browser maintains the state of checkboxes when moving
back/forward, our page needs to respond by applying the selected row
class
* The header state should also remain in sync
* Refactor so that shift clicking and normal clicking use the same code
paths
* Toggling a checkbox with shift click when no previous toggles have
been changed should work as normal
46222b5
@jennyd jennyd Style edit_multiple page with a table in a form 26bb872
@jennyd jennyd Choose archive or redirect for bulk editing
Display new url input on the edit multiple page if redirecting.
Include new_status hidden in the form to post to update.
d195ebc
@jennyd jennyd Save updates to multiple mappings
Various issues remain unresolved, relating to error handling, data
consistency and post-save redirect preserving filter and pagination.
421d3a5
@fofr fofr Fix radio button default and alignment
* The toggle on the checkbox in the header wasn’t specific enough and
was affecting the default state of the radio buttons
* Update module and tests to use js prefixed class names rather than
elements
*  Tweak alignment of radio buttons by removing padding and adding
margins
2a5ce99
@fofr fofr Explicity uncheck checkbox
* Adding the class attribute introduce a bug wherein the new parameter
evaluated to true and the input became checked.
3915572
@fofr fofr Keep edit multiple table consistent with mappings
* Use same table header text
* Use table header class
f5b81c5
@jennyd jennyd Test that only users who can edit site can edit_multiple 49d1168
@jennyd jennyd Test redirect if no mapping ids for site received e057d90
@jennyd jennyd Test edit_multiple redirect with invalid new status 470ab1e
@jennyd jennyd Test that only users who can edit site can update_multiple 0a5f570
@jennyd jennyd Test update_multiple redirect if no mapping ids for site 6357d96
@jennyd jennyd Use '301'/'410' for form values
This is a bit simpler and more consistent than using
'redirect'/'archive' in forms.
9c3ecd8
@jennyd jennyd Fix comment for options_for_supported_statuses
This mappings helper method was changed in 62a5440 and the comment now
matches its existing behaviour.
e2f6b2a
@jennyd jennyd Use 'http_status' consistently instead of 'new_status' d82dac0
@fofr fofr Refactor selectable table into its own file
* Separate table and mappings, avoid grouping behaviour based on where
it’s being used
* Rename spec to use _ to match file conventions
7ae7dad
@fofr fofr Split edit_multiple template into partials
* Break into parts for re-use in a modal
* Create an edit multiple modal
* Modify table slightly based on modal local variable passed in
fca3e5f
@fofr fofr Embolden 'edit selected' button
* Matches styles of other non-input type buttons
7b66b45
@fofr fofr Return bulk edit table via ajax, display in modal
* Put back previously removed jquery_ujs so that ‘remote: true’ works
as expected
* Use javascript hooks to select an input type and submit the edit
multiple form, displaying the resulting HTML in a bootstrap modal window
* When the modal window is hidden remove it from the dom
* Extend selectable table module to include the wrapping form
* Known issues: Submitting the form without anything selected breaks
the modal. There’s no ajax loading indicator or error handling.
f7039c7
@jennyd jennyd Add test for http_status_name helper method 510917d
@jennyd jennyd Actually fix comment for options_for_supported_statuses
This still had the official description for the codes, rather than
our more user-friendly ones.
907560e
@fofr fofr Rename mapping_index feature mapping_edit_feature 40e62f2
@fofr fofr Add modal scenarios to edit multiple feature
* Test for open modal and check for correct modal content
* Test form submits and saves correctly
* Add a modals section to page assertion steps
* Refine hidden input assertion
d564b61
@jennyd jennyd Validate data before attempting to update any mappings
Since mapping validation is complex and may change in the future,
the simplest way of validating the submitted data is to build a new
mapping with it and check if the mapping is valid.
42ddced
@jennyd jennyd Display error message if new URL is invalid cc0b9db
@jennyd jennyd Display error message if not all mappings were updated b31b84e
@fofr fofr Disable submit buttons when appropriate
* Prevent multiple ajax requests by disabling buttons when a request is
made, and re-enabling on completion (whether error or success)
* Also toggle the disabled state based on whether rows have been
selected
* Use Bootstraps pattern for showing loading text on a button
* Add error handlers for the ajax response
777e4ea
@fofr fofr Unfix the header
* We are going to fix other parts of the page
e63286f
@fofr fofr Update style guide with seletable table changes 14160af
@jennyd jennyd Store site index URL on session and redirect to it
It is usually useful to preserve the path filter and pagination
when redirecting back to the site mappings index. This does mean
that the session will store return URLs for all sites which the
user looks at the mappings for.
89c2b57
@fofr fofr Place table actions in footer too
* Use td elements rather than th, the cells are not column headers, but
by being in thead and tfoot they have the appropriate semantics.
* Move actions into a partial and include both in thead and tfoot
229c152
@fofr fofr Don't repeat the form elements in the footer
* Repeating the radio buttons causes interaction problems
* As the footer now has no use when JS is disabled, hide it from view
2d9d45a
@fofr fofr Fix ambiguity in mappings feature
* Specify that interactions should be with the first instance of a
button or link
bdcca70
@jennyd jennyd Factor out setting of @mappings and @http_status
Both edit_multiple and update_multiple need these, and need to check
errors for them in the same way.
1ea2096
@jennyd jennyd Test redirecting to site index with filter better
Instead of artificially storing it on the session in the test, get
the site index with the filter param as the user would.
b316aa9
@jennyd jennyd One-line setting of new_url variables 2b7c6a6
@jennyd jennyd Factor out site_return_url_key f375097
@fofr fofr Use table header styles consistently
* Add styles to orgs, sites and hits tables
358053b
@jennyd jennyd Test that update_multiple updates mappings correctly cdb7185
@jennyd jennyd Test that versions are created by update_multiple 6f67019
@jennyd jennyd Simplify test for update_multiple updating mappings 5fa2eba
@jennyd jennyd If some updates fail, display only those again
This is difficult to test, because it would involve creating an
invalid mapping to attempt to update, but it's a real edge case.
The only way I can think of for some mappings to not be updateable
is if we accidentally import a homepage mapping (bypassing validation
in the import) which is then invalid because it doesn't have a path,
and so would not be saved with the new data.
aed3123
@jennyd jennyd Remove unnecessary encoding from spec file 9c9aaa7
@jennyd jennyd Remove FIXME 8bfa6ac
@jennyd jennyd Fix cancel button after submitting 2 invalid URLs
This button was previously trying to go back, which would fail if
two invalid URLs were submitted, because it would be trying to go
back to an action which only allows post requests. The button now
links to the site's mappings index preserving pagination and
filters.
13e1cf1
@jennyd jennyd Slightly clearer message if mappings can't be updated 48929e5
@rgarner rgarner commented on an outdated diff Dec 18, 2013
features/mapping_edit_multiple.feature
@@ -0,0 +1,82 @@
+Feature: Editing multiple mappings for a site
+ As a GDS User,
+ I want to update many existing mappings at once
+ so that I can efficiently improve the quailty of mappings
@rgarner
rgarner Dec 18, 2013 Contributor

Mmm, quailty 🥚

@jamiecobbett
Contributor

As a rule, I'd much prefer to see big things like this broken down. In this case, I might have split it into a non-JS Pull Request and then a JS enhancement.

@fofr fofr Quality not quailty
* Fowl begone
b23a613
@rgarner rgarner commented on the diff Dec 18, 2013
features/mapping_edit_multiple.feature
@@ -0,0 +1,82 @@
+Feature: Editing multiple mappings for a site
+ As a GDS User,
+ I want to update many existing mappings at once
+ so that I can efficiently improve the quailty of mappings
+
+ Background:
+ Given I have logged in as an admin
+ And there is a site called directgov belonging to an organisation directgov with these mappings:
+ | http_status | path | new_url |
+ | 301 | /a | http://gov.uk/directgov |
+ | 301 | /about/branding | http://gov.uk/branding |
+ | 410 | /about/corporate | |
+ And I visit the path /sites/directgov/mappings
+
@rgarner
rgarner Dec 18, 2013 Contributor

The next block's getting a bit too implementation-specific. This will make the tests resistant to change. Comments inline about how we might be less specific about implementation without altering the goal (while also acknowledging that I've been guilty of similar elsewhere and pledging to reform my character):

@rgarner rgarner commented on an outdated diff Dec 18, 2013
features/mapping_edit_multiple.feature
+Feature: Editing multiple mappings for a site
+ As a GDS User,
+ I want to update many existing mappings at once
+ so that I can efficiently improve the quailty of mappings
+
+ Background:
+ Given I have logged in as an admin
+ And there is a site called directgov belonging to an organisation directgov with these mappings:
+ | http_status | path | new_url |
+ | 301 | /a | http://gov.uk/directgov |
+ | 301 | /about/branding | http://gov.uk/branding |
+ | 410 | /about/corporate | |
+ And I visit the path /sites/directgov/mappings
+
+ Scenario: Selecting multiple mappings to redirect without javascript
+ When I click on the checkboxes for the first and second mappings
@rgarner
rgarner Dec 18, 2013 Contributor
  When I select the first two mappings
@rgarner rgarner commented on an outdated diff Dec 18, 2013
features/mapping_edit_multiple.feature
+ As a GDS User,
+ I want to update many existing mappings at once
+ so that I can efficiently improve the quailty of mappings
+
+ Background:
+ Given I have logged in as an admin
+ And there is a site called directgov belonging to an organisation directgov with these mappings:
+ | http_status | path | new_url |
+ | 301 | /a | http://gov.uk/directgov |
+ | 301 | /about/branding | http://gov.uk/branding |
+ | 410 | /about/corporate | |
+ And I visit the path /sites/directgov/mappings
+
+ Scenario: Selecting multiple mappings to redirect without javascript
+ When I click on the checkboxes for the first and second mappings
+ And I submit the form with the "Edit selected" button
@rgarner
rgarner Dec 18, 2013 Contributor
  And I go to edit my selection
@rgarner rgarner commented on an outdated diff Dec 18, 2013
features/mapping_edit_multiple.feature
+ Background:
+ Given I have logged in as an admin
+ And there is a site called directgov belonging to an organisation directgov with these mappings:
+ | http_status | path | new_url |
+ | 301 | /a | http://gov.uk/directgov |
+ | 301 | /about/branding | http://gov.uk/branding |
+ | 410 | /about/corporate | |
+ And I visit the path /sites/directgov/mappings
+
+ Scenario: Selecting multiple mappings to redirect without javascript
+ When I click on the checkboxes for the first and second mappings
+ And I submit the form with the "Edit selected" button
+ Then the page title should be "Redirect mappings"
+ And I should see "/a"
+ And I should see "/about/branding"
+ And I should have 2 hidden inputs for mapping IDs
@rgarner
rgarner Dec 18, 2013 Contributor

This is pure implementation - it might be the only convenient place to test it, but I'm not sure it should be here.

@rgarner rgarner commented on an outdated diff Dec 18, 2013
features/mapping_edit_multiple.feature
+ And there is a site called directgov belonging to an organisation directgov with these mappings:
+ | http_status | path | new_url |
+ | 301 | /a | http://gov.uk/directgov |
+ | 301 | /about/branding | http://gov.uk/branding |
+ | 410 | /about/corporate | |
+ And I visit the path /sites/directgov/mappings
+
+ Scenario: Selecting multiple mappings to redirect without javascript
+ When I click on the checkboxes for the first and second mappings
+ And I submit the form with the "Edit selected" button
+ Then the page title should be "Redirect mappings"
+ And I should see "/a"
+ And I should see "/about/branding"
+ And I should have 2 hidden inputs for mapping IDs
+ And I should see a "Redirect to" input
+ But I should not see "/about/corporate"
@rgarner
rgarner Dec 18, 2013 Contributor

I might replace all of 18-23 with

  Then I should see an editing form with title "Redirect to" containing only my selections

Things like the "I should see" parts, if they're important, can move into the step. Likewise the testing for hidden input parts can move into the step.

@jamiecobbett jamiecobbett and 1 other commented on an outdated diff Dec 18, 2013
app/helpers/mappings_helper.rb
@@ -51,10 +51,17 @@ def mapping_edit_tabs(options = {})
##
# Return a FormBuilder-compatible list of HTTP Status codes with descriptions
- # e.g. [['301 Moved Permanently', '301'], ['410 Gone', '410']]
+ # e.g. [['Archive', '301'], ['Redirect', '410']]
@jamiecobbett
jamiecobbett Dec 18, 2013 Contributor

This example is the wrong way around

@jennyd
jennyd Dec 18, 2013 Member

D'oh. Third time lucky...

@jamiecobbett jamiecobbett and 2 others commented on an outdated diff Dec 18, 2013
app/views/mappings/edit_multiple_modal.html.erb
@@ -0,0 +1,15 @@
+<%= form_tag update_multiple_site_mappings_path(@site), id: 'bulk-editing-modal', class: 'modal hide' do %>
+ <header class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+ <h3>
+ <%= http_status_name(@http_status) %> mappings
+ </h3>
+ </header>
+ <div class="modal-body">
+ <%= render partial: 'edit_multiple_table', locals: { modal: true, mappings: @mappings, site: @site, new_url: @new_url, new_url_error: @new_url_error, http_status: @http_status } %>
@jamiecobbett
jamiecobbett Dec 18, 2013 Contributor

Would it make sense for this to be inside the footer? Then when you have a large list, you wouldn't need to scroll down past them to see the New URL box.

@fofr
fofr Dec 18, 2013 Member

@jennyd and I have discussed switching these two fields around, which I think makes sense

@jennyd
jennyd Dec 18, 2013 Member

Yes, I think that input should be more obvious in the modal - it's easy to miss at the moment.

@fofr
fofr Dec 18, 2013 Member

I've swapped these around, but only in the modal. See 1d78970

@jamiecobbett jamiecobbett commented on an outdated diff Dec 18, 2013
spec/controllers/mappings_controller_spec.rb
+
+ it 'updates the mappings correctly' do
+ [mapping_a, mapping_b].each do |mapping|
+ mapping.reload
+ expect(mapping.http_status).to eql('301')
+ expect(mapping.new_url).to eql('http://www.example.com')
+ end
+ end
+
+ it 'does not update other mappings' do
+ mapping_c.reload
+ expect(mapping_c.http_status).to eql('410')
+ expect(mapping_c.new_url).to be_nil
+ end
+
+ it 'redirects to the last-visited site index page' do
@jamiecobbett
jamiecobbett Dec 18, 2013 Contributor

There are several places in this file where you've said "site index" but you mean "mappings index".

@rgarner rgarner and 1 other commented on an outdated diff Dec 18, 2013
app/controllers/mappings_controller.rb
+ error = mappings_or_status_error
+ return error if error
+
+ if request.xhr?
+ render 'edit_multiple_modal', layout: nil
+ end
+ end
+
+ def update_multiple
+ set_vars_from_params_for_edit_multiple
+ error = mappings_or_status_error
+ return error if error
+
+ @update_data = { http_status: @http_status }
+ if @http_status == '301'
+ @update_data[:new_url] = @new_url = params[:new_url]
@rgarner
rgarner Dec 18, 2013 Contributor

@update_data isn't used in any view I can see. It should just be a variable, and in the one case it's required elsewhere (bulk_update_mappings) passed as a parameter. To save time, you could cherry-pick 6cd6a6d7b34b844ff0c093a7b605e6826b703e02

@jennyd
jennyd Dec 18, 2013 Member

Thanks 🍒

jennyd and others added some commits Dec 18, 2013
@jennyd jennyd Be more consistent with 'mappings index' in tests 456d604
@rgarner @fofr rgarner @update_data not used in a view, shouldn't be a field 7a48f12
@fofr fofr Put redirect above table of mappings in modal
* Break the redirect field and logic out of the edit_multiple_table
partial and into its own
* Change the order of the elements when being shown in a modal
0ac1574
@fofr fofr Fix checkbox & radio non-javascript interactions
* Hide check-all checkbox when we aren’t using JavaScript, it won’t do
anything
* Update the label ‘for’ attribute so that labels are clickable again
d44d39e
@fofr fofr Make edit features less implementation-specific
* Refer to selecting mappings, not clicking checkboxes
* Refer to editing a selection, not clicking a specific button
* Refer to a form containing a selection, not a list of URLs
* Refer to saving changes, rather than clicking a save button

(Mostly implementing @rgarner’s suggestions)
998d5fd
@rgarner @jennyd rgarner Get bulk update concerns out of MappingsController
* Helps edit/update_multiple read ok
* May violate SRP slightly, but probably less than we were doing
b06d063
@rgarner rgarner General tidyings:
* Make sure would_fail_on_url? works not just by coincidence
* Tidy test_mapping
* Whitespace in update_multiple
* Better naming for failed_updates (becomes failures)
* Rename would_fail_on_url? to would_fail_on_new_url?
59703f1
@rgarner rgarner Reduce number of fields in modal views 179e29c
@rgarner rgarner Reduce number of fields in modal views, pt 2 b27ec77
@jennyd jennyd Merge pull request #120 from alphagov/view-model-bulk
Tidy the BulkEditor
b084813
@jamiecobbett
Contributor

Awesome work, @fofr and @jennyd. Its great to have this feature in :)

@jamiecobbett jamiecobbett merged commit e291050 into master Dec 20, 2013
@jamiecobbett jamiecobbett deleted the bulk-editing branch Dec 20, 2013
@jamiecobbett
Contributor

1,340 lines MERGINATED

@rgarner
Contributor
rgarner commented Dec 20, 2013

🍖 🍈 📣 📝 🚹

Ok, who moved :merginator:?

@jamiecobbett
Contributor

YIPPY-KI-MERGED

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment