Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions app/components/tab_navigation_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<%= content_tag(:nav, aria: { label: }, **tag_options) do %>
<ul class="usa-button-group usa-button-group--segmented">
<% routes.each do |route| %>
<li class="usa-button-group__item grid-col display-flex">
<%= render ButtonComponent.new(
action: ->(**tag_options, &block) { link_to(route[:path], **tag_options, &block) },
big: true,
outline: !is_current_path?(route[:path]),
aria: { current: is_current_path?(route[:path]) ? 'page' : nil },
class: 'grid-col',
).with_content(route[:text]) %>
</li>
<% end %>
</ul>
<% end %>
15 changes: 15 additions & 0 deletions app/components/tab_navigation_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class TabNavigationComponent < BaseComponent
attr_reader :label, :routes, :tag_options

def initialize(label:, routes:, **tag_options)
@label = label
@routes = routes
@tag_options = tag_options
end

def is_current_path?(path)
request.path == URI.parse(path).path
rescue URI::InvalidURIError
false
end
end
5 changes: 5 additions & 0 deletions app/controllers/concerns/sign_in_a_b_test_concern.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module SignInABTestConcern
def sign_in_a_b_test_bucket
AbTests::SIGN_IN.bucket(sp_session[:request_id] || session.id)
end
end
6 changes: 5 additions & 1 deletion app/controllers/sign_up/completions_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module SignUp
class CompletionsController < ApplicationController
include SecureHeadersConcern
include SignInABTestConcern

before_action :confirm_two_factor_authenticated
before_action :verify_confirmed, if: :ial2?
Expand Down Expand Up @@ -84,7 +85,10 @@ def analytics_attributes(page_occurence)
end

def track_completion_event(last_page)
analytics.user_registration_complete(**analytics_attributes(last_page))
analytics.user_registration_complete(
**analytics_attributes(last_page),
sign_in_a_b_test_bucket:,
)
end

def pii
Expand Down
7 changes: 6 additions & 1 deletion app/controllers/sign_up/registrations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module SignUp
class RegistrationsController < ApplicationController
include PhoneConfirmation
include ApplicationHelper # for ial2_requested?
include SignInABTestConcern

before_action :confirm_two_factor_authenticated, only: [:destroy_confirm]
before_action :require_no_authentication
Expand All @@ -14,7 +15,11 @@ def new
analytics: analytics,
attempts_tracker: irs_attempts_api_tracker,
)
analytics.user_registration_enter_email_visit
@sign_in_a_b_test_bucket = sign_in_a_b_test_bucket
analytics.user_registration_enter_email_visit(
sign_in_a_b_test_bucket: @sign_in_a_b_test_bucket,
from_sign_in: params[:source] == 'sign_in',
)
render :new, locals: { request_id: nil }, formats: :html
end

Expand Down
11 changes: 7 additions & 4 deletions app/controllers/users/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class SessionsController < Devise::SessionsController
include RememberDeviceConcern
include Ial2ProfileConcern
include Api::CsrfTokenConcern
include SignInABTestConcern

rescue_from ActionController::InvalidAuthenticityToken, with: :redirect_to_signin

Expand All @@ -19,15 +20,17 @@ class SessionsController < Devise::SessionsController
after_action :add_csrf_token_header_to_response, only: [:keepalive]

def new
analytics.sign_in_page_visit(
flash: flash[:alert],
stored_location: session['user_return_to'],
)
override_csp_for_google_analytics

@request_id = request_id_if_valid
@ial = sp_session_ial
@browser_is_ie11 = browser_is_ie11?
@sign_in_a_b_test_bucket = sign_in_a_b_test_bucket
analytics.sign_in_page_visit(
flash: flash[:alert],
stored_location: session['user_return_to'],
sign_in_a_b_test_bucket: @sign_in_a_b_test_bucket,
)
super
end

Expand Down
18 changes: 15 additions & 3 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2701,12 +2701,14 @@ def session_total_duration_timeout

# @param [String] flash
# @param [String] stored_location
# @param [String] sign_in_a_b_test_bucket
# tracks when a user visits the sign in page
def sign_in_page_visit(flash:, stored_location:, **extra)
def sign_in_page_visit(flash:, stored_location:, sign_in_a_b_test_bucket:, **extra)
track_event(
'Sign in page visited',
flash: flash,
stored_location: stored_location,
sign_in_a_b_test_bucket:,
**extra,
)
end
Expand Down Expand Up @@ -2952,8 +2954,15 @@ def webauthn_setup_visit(platform_authenticator:, errors:, enabled_mfa_methods_c
end

# Tracks when user visits enter email page
def user_registration_enter_email_visit
track_event('User Registration: enter email visited')
# @param [String] sign_in_a_b_test_bucket
# @param [Boolean] from_sign_in
def user_registration_enter_email_visit(sign_in_a_b_test_bucket:, from_sign_in:, **extra)
track_event(
'User Registration: enter email visited',
sign_in_a_b_test_bucket:,
from_sign_in:,
**extra,
)
end

# @param [Integer] enabled_mfa_methods_count
Expand Down Expand Up @@ -2982,13 +2991,15 @@ def user_registration_cancellation(request_came_from:, **extra)
# @param [String] service_provider_name
# @param [String] page_occurence
# @param [String] needs_completion_screen_reason
# @param [String] sign_in_a_b_test_bucket
# @param [Array] sp_request_requested_attributes
# @param [Array] sp_session_requested_attributes
def user_registration_complete(
ial2:,
service_provider_name:,
page_occurence:,
needs_completion_screen_reason:,
sign_in_a_b_test_bucket:,
sp_session_requested_attributes:,
sp_request_requested_attributes: nil,
ialmax: nil,
Expand All @@ -3001,6 +3012,7 @@ def user_registration_complete(
service_provider_name: service_provider_name,
page_occurence: page_occurence,
needs_completion_screen_reason: needs_completion_screen_reason,
sign_in_a_b_test_bucket:,
sp_request_requested_attributes: sp_request_requested_attributes,
sp_session_requested_attributes: sp_session_requested_attributes,
**extra,
Expand Down
28 changes: 21 additions & 7 deletions app/views/devise/sessions/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,23 @@

<% if decorated_session.sp_name %>
<%= render 'sign_up/registrations/sp_registration_heading' %>
<% else %>
<% elsif @sign_in_a_b_test_bucket == :default %>
<%= render PageHeadingComponent.new.with_content(decorated_session.new_session_heading) %>
<% end %>

<% if @sign_in_a_b_test_bucket == :tabbed %>
<%= render TabNavigationComponent.new(
label: t('account.login.tab_navigation'),
routes: [
{ text: t('links.next'), path: new_user_session_url(request_id: @request_id) },
{ text: t('links.create_account'), path: sign_up_email_url(request_id: @request_id, source: :sign_in) },
],
class: 'margin-bottom-4',
) %>

<%= render PageHeadingComponent.new.with_content(t('headings.sign_in_existing_users')) %>
<% end %>

<%= render 'shared/sp_alert', section: 'sign_in' %>

<%= simple_form_for(
Expand All @@ -35,20 +49,20 @@
field_options: { required: true },
) %>
<%= f.input :request_id, as: :hidden, input_html: { value: @request_id } %>
<div class='margin-bottom-4'>
<%= f.submit t('links.next'), full_width: true, wide: false %>
<%= f.submit t('links.next'), full_width: true, wide: false %>
<% if @sign_in_a_b_test_bucket == :default %>
<h2 class='separator-text'>
<%= t('headings.create_account_with_sp.cta', app_name: APP_NAME) %>
</h2>
<%= link_to(
t('links.create_account'),
sign_up_email_url(request_id: @request_id),
class: 'usa-button usa-button--big usa-button--outline usa-button--full-width margin-top-1',
sign_up_email_url(request_id: @request_id, source: :sign_in),
class: 'usa-button usa-button--big usa-button--outline usa-button--full-width margin-bottom-105',
) %>
</div>
<% end %>
<% end %>
<% if @ial && desktop_device? %>
<div class='margin-x-neg-1 margin-y-4'>
<div class='margin-x-neg-1 margin-top-205'>
<%= link_to(
t('account.login.piv_cac'),
login_piv_cac_url,
Expand Down
19 changes: 18 additions & 1 deletion app/views/sign_up/registrations/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,24 @@

<%= render 'shared/sp_alert', section: 'sign_up' %>

<%= render PageHeadingComponent.new.with_content(t('titles.registrations.new')) %>
<% if @sign_in_a_b_test_bucket == :tabbed %>
<% if decorated_session.sp_name %>
<%= render 'sign_up/registrations/sp_registration_heading' %>
<% end %>

<%= render TabNavigationComponent.new(
label: t('account.login.tab_navigation'),
routes: [
{ text: t('links.next'), path: new_user_session_url(request_id: sp_session[:request_id]) },
{ text: t('links.create_account'), path: sign_up_email_path(request_id: sp_session[:request_id]) },
],
class: 'margin-bottom-4',
) %>

<%= render PageHeadingComponent.new.with_content(t('headings.create_account_new_users')) %>
<% else %>
<%= render PageHeadingComponent.new.with_content(t('titles.registrations.new')) %>
<% end %>

<%= simple_form_for(
@register_user_email_form,
Expand Down
1 change: 1 addition & 0 deletions config/application.yml.default
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ session_timeout_warning_seconds: 150
session_total_duration_timeout_in_minutes: 720
ses_configuration_set_name: ''
set_remember_device_session_expiration: false
sign_in_a_b_testing: '{"default":100,"tabbed":0}'
sp_handoff_bounce_max_seconds: 2
show_user_attribute_deprecation_warnings: false
otp_min_attempts_remaining_warning_count: 3
Expand Down
5 changes: 5 additions & 0 deletions config/initializers/ab_tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ module AbTests
},
)

SIGN_IN = AbTestBucket.new(
experiment_name: 'Sign In Experience',
buckets: IdentityConfig.store.sign_in_a_b_testing,
)

def self.in_person_cta_variant_testing_buckets
buckets = Hash.new
percents = IdentityConfig.store.in_person_cta_variant_testing_percents
Expand Down
1 change: 1 addition & 0 deletions config/locales/account/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ en:
login:
ie_not_supported: Internet Explorer 11 is no longer supported as of %{date}
piv_cac: Sign in with your government employee ID
tab_navigation: Account creation tabs
navigation:
access_services: Access your government benefits and services from your
%{app_name} account.
Expand Down
1 change: 1 addition & 0 deletions config/locales/account/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ es:
login:
ie_not_supported: Internet Explorer 11 dejó de ser compatible a partir del %{date}
piv_cac: Inicie sesión con su identificación de empleado del gobierno
tab_navigation: Pestañas de creación de cuenta
navigation:
access_services: Acceda a los beneficios y servicios de su gobierno desde su
cuenta %{app_name}.
Expand Down
1 change: 1 addition & 0 deletions config/locales/account/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ fr:
login:
ie_not_supported: Internet Explorer 11 n’est plus pris en charge à partir du %{date}
piv_cac: Connectez-vous avec votre ID d’employé du gouvernement
tab_navigation: Onglets de création de compte
navigation:
access_services: Accédez à vos avantages et services gouvernementaux depuis
votre compte %{app_name}.
Expand Down
2 changes: 2 additions & 0 deletions config/locales/headings/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ en:
prompt: Are you sure you want to cancel?
confirmations:
new: Send another confirmation email
create_account_new_users: Create an account for new users
create_account_with_sp:
cta: First time using %{app_name}?
sp_text: is using %{app_name} to allow you to sign in to your account safely and
Expand Down Expand Up @@ -60,6 +61,7 @@ en:
new: Use your PIV/CAC card to secure your account
residential_address: Current residential address
session_timeout_warning: Need more time?
sign_in_existing_users: Sign in for existing users
sign_in_with_sp: Sign in to continue to %{sp}
sign_in_without_sp: Sign in
sp_handoff_bounced: There was a problem connecting to %{sp_name}
Expand Down
2 changes: 2 additions & 0 deletions config/locales/headings/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ es:
prompt: '¿Estas seguro que quieres cancelar?'
confirmations:
new: Enviar otro email de confirmación
create_account_new_users: Crear una cuenta para usuarios nuevos
create_account_with_sp:
cta: '¿Es la primera vez que utiliza %{app_name}?'
sp_text: está utilizando %{app_name} para permitirle iniciar sesión en su cuenta
Expand Down Expand Up @@ -60,6 +61,7 @@ es:
new: Use su tarjeta PIV/CAC para asegurar su cuenta
residential_address: Dirección residencial actual
session_timeout_warning: '¿Necesita más tiempo?'
sign_in_existing_users: Iniciar sesión para usuarios existentes
sign_in_with_sp: Iniciar sesión para continuar con %{sp}
sign_in_without_sp: Iniciar sesión
sp_handoff_bounced: Hubo un problema al conectarse a %{sp_name}
Expand Down
2 changes: 2 additions & 0 deletions config/locales/headings/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ fr:
prompt: Es-tu sûre de vouloir annuler?
confirmations:
new: Envoyer un autre courriel de confirmation
create_account_new_users: Créer un compte pour les nouveaux utilisateurs
create_account_with_sp:
cta: Première fois que vous utilisez %{app_name}?
sp_text: utilise %{app_name} pour vous permettre de vous connecter à votre
Expand Down Expand Up @@ -63,6 +64,7 @@ fr:
new: Utilisez votre carte PIV/CAC pour sécuriser votre compte
residential_address: Adresse de résidence actuelle
session_timeout_warning: Vous avez besoin de plus de temps?
sign_in_existing_users: S’identifier pour les utilisateurs existants
sign_in_with_sp: Connectez-vous pour continuer à %{sp}
sign_in_without_sp: Connexion
sp_handoff_bounced: Un problème est survenu lors de la connexion à %{sp_name}
Expand Down
1 change: 1 addition & 0 deletions lib/identity_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ def self.build_store(config_map)
config.add(:ses_configuration_set_name, type: :string)
config.add(:set_remember_device_session_expiration, type: :boolean)
config.add(:show_user_attribute_deprecation_warnings, type: :boolean)
config.add(:sign_in_a_b_testing, type: :json, options: { symbolize_names: true })
config.add(:skip_encryption_allowed_list, type: :json)
config.add(:sp_handoff_bounce_max_seconds, type: :integer)
config.add(:state_tracking_enabled, type: :boolean)
Expand Down
30 changes: 30 additions & 0 deletions spec/components/previews/tab_navigation_component_preview.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
class TabNavigationComponentPreview < BaseComponentPreview
# @!group Preview
def default
render TabNavigationComponent.new(
label: 'Navigation',
routes: [
{ path: lookbook_path('preview'), text: 'Preview' },
{ path: lookbook_path('workbench'), text: 'Workbench' },
],
)
end
# @!endgroup

# @param label text
def workbench(label: 'Navigation')
render TabNavigationComponent.new(
label:,
routes: [
{ path: lookbook_path('preview'), text: 'Preview' },
{ path: lookbook_path('workbench'), text: 'Workbench' },
],
)
end

private

def lookbook_path(example)
Lookbook::Engine.routes.url_helpers.lookbook_preview_path("tab_navigation/#{example}")
end
end
Loading