Skip to content

Commit

Permalink
Merge pull request #2424 from alphagov/use-option-select-vs-checkboxe…
Browse files Browse the repository at this point in the history
…s-for-granting-permissions

Use option-select vs checkboxes for granting permissions
  • Loading branch information
floehopper authored Oct 11, 2023
2 parents c60b886 + 7eb14ee commit 4bf9257
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 83 deletions.
4 changes: 4 additions & 0 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ def sanitised_fullpath
uri.query_values = uri.query_values.reject { |key, _value| SENSITIVE_QUERY_PARAMETERS.include?(key) }
uri.to_s
end

def with_checked_options_at_top(options)
options.sort_by { |o| o[:checked] ? 0 : 1 }
end
end
8 changes: 1 addition & 7 deletions app/helpers/users_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,22 +75,16 @@ def options_for_organisation_select(selected: nil)
end
end

def items_for_permission_checkboxes(application:, user: nil)
def options_for_permission_option_select(application:, user: nil)
application.sorted_supported_permissions_grantable_from_ui.map do |permission|
{
id: supported_permission_checkbox_id(application, permission),
name: "user[supported_permission_ids][]",
label: formatted_permission_name(application.name, permission.name),
value: permission.id,
checked: user&.has_permission?(permission),
}
end
end

def supported_permission_checkbox_id(application, permission)
"user_application_#{application.id}_supported_permission_#{permission.id}"
end

def formatted_permission_name(application_name, permission_name)
if permission_name == SupportedPermission::SIGNIN_NAME
"Has access to #{application_name}?"
Expand Down
4 changes: 0 additions & 4 deletions app/models/users_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ class UsersFilter

attr_reader :options

def self.with_checked_at_top(options)
options.sort_by { |o| o[:checked] ? 0 : 1 }
end

def initialize(users, current_user, options = {})
@users = users
@current_user = current_user
Expand Down
27 changes: 11 additions & 16 deletions app/views/batch_invitation_permissions/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,17 @@
%>

<%= form_for @batch_invitation, url: :batch_invitation_permissions, method: :post do |f| %>
<%= render "govuk_publishing_components/components/accordion", {
items: policy_scope(:user_permission_manageable_application).reject(&:retired?).map do |application|
{
heading: {
text: application.name
},
content: {
html: render("govuk_publishing_components/components/checkboxes", {
name: "permissions_for_#{application.id}",
heading: "Permissions for #{application.name}",
items: items_for_permission_checkboxes(application:),
})
},
}
end
} %>
<% policy_scope(:user_permission_manageable_application).reject(&:retired?).map do |application| %>
<% options = options_for_permission_option_select(application:) %>
<%= render("govuk_publishing_components/components/option_select", {
title: application.name,
key: "user[supported_permission_ids]",
options_container_id: "user_application_#{application.id}_supported_permissions",
show_filter: options.length > 4,
closed_on_load: true,
options:,
}) %>
<% end %>

<%= render "govuk_publishing_components/components/button", { text: "Create users and send emails" } %>
<% end %>
27 changes: 11 additions & 16 deletions app/views/devise/invitations/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -86,22 +86,17 @@
heading_size: "l",
} do %>

<%= render "govuk_publishing_components/components/accordion", {
items: policy_scope(:user_permission_manageable_application).reject(&:retired?).map do |application|
{
heading: {
text: application.name
},
content: {
html: render("govuk_publishing_components/components/checkboxes", {
name: "permissions_for_#{application.id}",
heading: "Permissions for #{application.name}",
items: items_for_permission_checkboxes(application:, user: f.object),
})
},
}
end
} %>
<% policy_scope(:user_permission_manageable_application).reject(&:retired?).map do |application| %>
<% options = options_for_permission_option_select(application:, user: f.object) %>
<%= render("govuk_publishing_components/components/option_select", {
title: application.name,
key: "user[supported_permission_ids]",
options_container_id: "user_application_#{application.id}_supported_permissions",
show_filter: options.length > 4,
closed_on_load: options.none? { |o| o[:checked] },
options: with_checked_options_at_top(options),
}) %>
<% end %>

<% end %>

Expand Down
4 changes: 2 additions & 2 deletions app/views/users/_users_filter.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
options_container_id: "organisations_filter",
show_filter: true,
closed_on_load: @filter.no_options_selected_for?(:organisations),
options: UsersFilter.with_checked_at_top(
options: with_checked_options_at_top(
@filter.organisation_option_select_options(aria_controls_id: filtered_users_id)
),
} %>
Expand All @@ -60,7 +60,7 @@
options_container_id: "permissions_filter",
show_filter: true,
closed_on_load: @filter.no_options_selected_for?(:permissions),
options: UsersFilter.with_checked_at_top(
options: with_checked_options_at_top(
@filter.permission_option_select_options(aria_controls_id: filtered_users_id)
),
} %>
Expand Down
25 changes: 25 additions & 0 deletions test/controllers/batch_invitation_permissions_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,31 @@ class BatchInvitationPermissionsControllerTest < ActionController::TestCase
assert_select "label", "Has access to Profound Publisher?"
assert_select "label", "reader"
end

should "render form checkbox inputs for permissions" do
application = create(:application)
signin_permission = application.signin_permission
other_permission = create(:supported_permission)

get :new, params: { batch_invitation_id: @batch_invitation.id }

assert_select "form" do
assert_select "input[type='checkbox'][name='user[supported_permission_ids][]'][value='#{signin_permission.to_param}']"
assert_select "input[type='checkbox'][name='user[supported_permission_ids][]'][value='#{other_permission.to_param}']"
end
end

should "render filter for option-select component when app has more than 4 permissions" do
application = create(:application)
4.times { create(:supported_permission, application:) }
assert application.supported_permissions.count > 4

get :new, params: { batch_invitation_id: @batch_invitation.id }

assert_select "form" do
assert_select ".gem-c-option-select[data-filter-element]"
end
end
end

context "POST create" do
Expand Down
39 changes: 36 additions & 3 deletions test/controllers/invitations_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,29 @@ class InvitationsControllerTest < ActionController::TestCase
end
end

should "render form checkbox input for signin permission & select for other permissions" do
create(:application)
should "render form checkbox inputs for permissions" do
application = create(:application)
signin_permission = application.signin_permission
other_permission = create(:supported_permission)

get :new

assert_select "form" do
assert_select "input[type='checkbox'][name='user[supported_permission_ids][]'][value='#{signin_permission.to_param}']"
assert_select "input[type='checkbox'][name='user[supported_permission_ids][]'][value='#{other_permission.to_param}']"
end
end

should "render filter for option-select component when app has more than 4 permissions" do
application = create(:application)
4.times { create(:supported_permission, application:) }
assert application.supported_permissions.count > 4

get :new

assert_select "input[type='checkbox'][name='user[supported_permission_ids][]']"
assert_select "form" do
assert_select ".gem-c-option-select[data-filter-element]"
end
end
end

Expand Down Expand Up @@ -331,6 +348,22 @@ class InvitationsControllerTest < ActionController::TestCase
end
end

should "put selected permissions at the top if there are validation errors" do
application = create(:application)
signin_permission = application.signin_permission
other_permission = create(:supported_permission, application:)
selected_permissions = [other_permission]

post :create, params: { user: { supported_permission_ids: selected_permissions.map(&:to_param) } }

assert_select "form #user_application_#{application.id}_supported_permissions" do
assert_select "input[type='checkbox'][name='user[supported_permission_ids][]']" do |checkboxes|
assert_equal other_permission.to_param, checkboxes.first["value"]
assert_equal signin_permission.to_param, checkboxes.last["value"]
end
end
end

should "record account invitation in event log when invitation sent" do
EventLog.expects(:record_account_invitation).with(instance_of(User), @inviter)

Expand Down
20 changes: 20 additions & 0 deletions test/helpers/application_helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,24 @@ class ApplicationHelperTest < ActionView::TestCase
self.request = ActionDispatch::Request.new(Rack::MockRequest.env_for("/secret-squirrel?invitation_token=w1&reset_password_token=d1&sharing=ok"))
assert_equal "/secret-squirrel?sharing=ok", sanitised_fullpath
end

context "#with_checked_options_at_top" do
should "put all checked options before all unchecked options" do
options = [
{ label: "A", checked: false },
{ label: "B", checked: true },
{ label: "C", checked: false },
{ label: "D", checked: true },
]

expected_options = [
{ label: "B", checked: true },
{ label: "D", checked: true },
{ label: "A", checked: false },
{ label: "C", checked: false },
]

assert_equal expected_options, with_checked_options_at_top(options)
end
end
end
16 changes: 5 additions & 11 deletions test/helpers/users_helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,42 +76,36 @@ class UsersHelperTest < ActionView::TestCase
end
end

context "#items_for_permission_checkboxes" do
should "return permission options suitable for checkboxes component" do
context "#options_for_permission_option_select" do
should "return permission options suitable for option-select component" do
application = create(:application)
signin_permission = application.signin_permission
permission1 = create(:supported_permission, application:, name: "permission1")
permission2 = create(:supported_permission, application:, name: "permission2")

user = create(:user, supported_permissions: [signin_permission, permission1])

items = items_for_permission_checkboxes(application:, user:)
options = options_for_permission_option_select(application:, user:)

expected_items = [
expected_options = [
{
id: supported_permission_checkbox_id(application, signin_permission),
name: "user[supported_permission_ids][]",
label: "Has access to #{application.name}?",
value: signin_permission.id,
checked: true,
},
{
id: supported_permission_checkbox_id(application, permission1),
name: "user[supported_permission_ids][]",
label: "permission1",
value: permission1.id,
checked: true,
},
{
id: supported_permission_checkbox_id(application, permission2),
name: "user[supported_permission_ids][]",
label: "permission2",
value: permission2.id,
checked: false,
},
]

assert_equal expected_items, items
assert_equal expected_options, options
end
end

Expand Down
8 changes: 4 additions & 4 deletions test/integration/inviting_users_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,11 @@ class InvitingUsersTest < ActionDispatch::IntegrationTest
fill_in "Name", with: "Alicia Smith"
fill_in "Email", with: "alicia@example.com"

within_fieldset "Permissions for #{application_one.name}" do
within_fieldset application_one.name do
uncheck "Has access to #{application_one.name}?"
check "editor", allow_label_click: true
end
within_fieldset "Permissions for #{application_two.name}" do
within_fieldset application_two.name do
check "Has access to #{application_two.name}?"
uncheck "gds-admin", allow_label_click: true
end
Expand Down Expand Up @@ -322,11 +322,11 @@ class InvitingUsersTest < ActionDispatch::IntegrationTest
fill_in "Name", with: "Alicia Smith"
fill_in "Email", with: "alicia@example.com"

within_fieldset "Permissions for #{application_one.name}" do
within_fieldset application_one.name do
uncheck "Has access to #{application_one.name}?"
check "editor"
end
within_fieldset "Permissions for #{application_two.name}" do
within_fieldset application_two.name do
check "Has access to #{application_two.name}?"
uncheck "gds-admin"
end
Expand Down
20 changes: 0 additions & 20 deletions test/models/users_filter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,6 @@ class UsersFilterTest < ActiveSupport::TestCase
@current_user = User.new
end

context ".with_checked_at_top" do
should "put all checked options before all unchecked options" do
options = [
{ label: "A", checked: false },
{ label: "B", checked: true },
{ label: "C", checked: false },
{ label: "D", checked: true },
]

expected_options = [
{ label: "B", checked: true },
{ label: "D", checked: true },
{ label: "A", checked: false },
{ label: "C", checked: false },
]

assert_equal expected_options, UsersFilter.with_checked_at_top(options)
end
end

should "return all users in alphabetical name order" do
create(:user, name: "beta")
create(:user, name: "alpha")
Expand Down

0 comments on commit 4bf9257

Please sign in to comment.