Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9112edb
Allow SAMEORIGIN frames for environments with mailer previews enabled
aduth Jul 14, 2022
84694a0
LG-6343: Implement email template for "Ready to verify"
aduth Jul 14, 2022
d96c0bb
Add BarcodeComponent
aduth Jul 14, 2022
f4fc75e
Use BarcodeComponent in "ready to verify" views
aduth Jul 14, 2022
b1e676e
Add label to table element
aduth Jul 15, 2022
edb81b2
Remove unnecessary require statements from presenter
aduth Jul 15, 2022
ee976c3
Downgrade markup for email compatibility
aduth Jul 15, 2022
fbffdd6
Fix table spacing in web display
aduth Jul 15, 2022
44a4518
Sync markup to ready_to_verify controller
aduth Jul 15, 2022
9682e9e
Add translations for ready to verify email
aduth Jul 15, 2022
5235e05
Use distinct header for ready to verify email
aduth Jul 15, 2022
b748e3b
Parameterize greeting name
aduth Jul 15, 2022
f4650ba
Add Spanish intro text for ready to verify email
aduth Jul 15, 2022
854aced
Delete specs for removed methods
aduth Jul 15, 2022
63f44c0
Add specs for BarcodeComponent
aduth Jul 18, 2022
7830f02
Simplify email markup for template
aduth Jul 18, 2022
6684730
Adjust heading styles
aduth Jul 18, 2022
8bb5a45
Tighten up padding
aduth Jul 18, 2022
4dbc911
Use raster version of info icon
aduth Jul 18, 2022
52b2795
Standardize font size for alert, retail hours headings
aduth Jul 26, 2022
a781e96
Remove unused/redundant enrollment delegate
aduth Jul 26, 2022
946d268
Hide selected location when absent
aduth Jul 26, 2022
269cd09
Try nudging parallelization
aduth Jul 26, 2022
240183c
Parallelize 8
aduth Jul 26, 2022
cdc01c5
turn it up to 11
aduth Jul 27, 2022
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
2 changes: 1 addition & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ install:

specs:
stage: test
parallel: 7
parallel: 11
cache:
- <<: *ruby_cache
- <<: *yarn_cache
Expand Down
3 changes: 3 additions & 0 deletions app/assets/images/email/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Email Images

This folder contains images for exclusive use by mailer templates. This includes email-specific imagery, and also variants of existing assets. For example, since [SVG images are not well-supported](https://www.caniemail.com/features/image-svg/) in all email clients, this folder may include rasterized versions of common SVG images.
Binary file added app/assets/images/email/info.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions app/assets/stylesheets/components/_barcode.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.barcode.barby-barcode {
width: auto;
table-layout: fixed;
border-spacing: 0;
}

.barcode .barby-cell {
width: 2px;
height: 96px;

&.on {
background-color: #000;
}
}
1 change: 1 addition & 0 deletions app/assets/stylesheets/components/all.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@import 'account-header';
@import 'banner';
@import 'barcode';
@import 'block-link';
@import 'block-submit-button';
@import 'btn';
Expand Down
38 changes: 38 additions & 0 deletions app/assets/stylesheets/email.css.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
@import 'required';
@import 'variables/email';
@import 'foundation-emails/scss/foundation-emails';
@import 'identity-style-guide/dist/assets/scss/packages/required';
@import 'identity-style-guide/dist/assets/scss/packages/utilities';
@import './components/barcode';

.gray {
&:active,
Expand Down Expand Up @@ -118,3 +122,37 @@ h4 {
padding-right: $global-gutter-small !important;
}
}

.info-alert {
background-color: color('info-lighter');
padding: 0 units(0.5);

td {
padding: units(1.5);
padding-right: units(1);

& + td {
padding-left: 0;
padding-right: units(1.5);
}
}
}

.process-list td {
padding-bottom: units(4);
}

.process-list__circle {
border-radius: 50%;
width: units(3);
height: units(2.5);
background-color: color($theme-process-list-counter-background-color);
border: units($theme-process-list-counter-border-width) solid
color($theme-process-list-counter-border-color);
color: color('white');
font-size: units(2);
font-weight: 700;
text-align: center;
padding-top: units(0.5);
margin-right: units(1.5);
}
16 changes: 16 additions & 0 deletions app/components/barcode_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<%# Beware: This component is used in mailer content, so be mindful of email markup compatibility %>
<%= content_tag(
:div,
role: 'figure',
'aria-labelledby': barcode_caption_id,
class: css_class,
**tag_options,
) do %>
<%= barcode_html.html_safe %>
<div id="<%= barcode_caption_id %>" class="text-center margin-top-1">
<% if label.present? %>
<span class="usa-sr-only"><%= label %>:</span>
<% end %>
<%= formatted_data %>
</div>
<% end %>
38 changes: 38 additions & 0 deletions app/components/barcode_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require 'barby'
require 'barby/barcode/code_128'
require 'barby/outputter/html_outputter'

class BarcodeComponent < BaseComponent
attr_reader :barcode_data, :label, :label_formatter, :tag_options

def initialize(barcode_data:, label:, label_formatter: nil, **tag_options)
@barcode_data = barcode_data
@label = label
@label_formatter = label_formatter
@tag_options = tag_options
end

def formatted_data
formatted_data = barcode_data
formatted_data = label_formatter.call(formatted_data) if label_formatter
formatted_data
end

def barcode_html
html = Barby::Code128.new(barcode_data).to_html(class_name: 'barcode')
# The Barby gem doesn't provide much control over rendered output, so we need to manually slice
# in accessibility features (label as substitute to illegible inner content).
html.gsub(
'><tbody>',
%( aria-label="#{t('components.barcode.table_label')}"><tbody aria-hidden="true">),
)
end

def barcode_caption_id
"barcode-caption-#{unique_id}"
end

def css_class
[*tag_options[:class], 'display-inline-block margin-0']
end
end
12 changes: 12 additions & 0 deletions app/mailers/user_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,18 @@ def account_verified(user, email_address, date_time:, sp_name:, disavowal_token:
end
end

def in_person_ready_to_verify(user, email_address, first_name:, enrollment:)
with_user_locale(user) do
@header = t('in_person_proofing.headings.barcode')
@first_name = first_name
@presenter = Idv::InPerson::ReadyToVerifyPresenter.new(enrollment: enrollment)
mail(
to: email_address.email,
subject: t('user_mailer.in_person_ready_to_verify.subject', app_name: APP_NAME),
)
end
end

private

def email_should_receive_nonessential_notifications?(email)
Expand Down
15 changes: 1 addition & 14 deletions app/presenters/idv/in_person/ready_to_verify_presenter.rb
Original file line number Diff line number Diff line change
@@ -1,31 +1,19 @@
require 'barby'
require 'barby/barcode/code_128'
require 'barby/outputter/png_outputter'

module Idv
module InPerson
class ReadyToVerifyPresenter
# WILLFIX: With LG-6881, confirm timezone or use deadline from enrollment response.
USPS_SERVER_TIMEZONE = ActiveSupport::TimeZone['America/New_York']

delegate :selected_location_details, to: :enrollment
delegate :selected_location_details, :enrollment_code, to: :enrollment

def initialize(enrollment:)
@enrollment = enrollment
end

def barcode_data_url
Comment thread
tomas-nava marked this conversation as resolved.
"data:image/png;base64,#{Base64.strict_encode64(barcode_image_data)}"
end

def formatted_due_date
due_date.in_time_zone(USPS_SERVER_TIMEZONE).strftime(I18n.t('time.formats.event_date'))
end

def formatted_enrollment_code
EnrollmentCodeFormatter.format(enrollment_code)
end

def selected_location_hours(prefix)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The body of this method should change to the below, to accommodate the difference in format of selected_location_details noted in previous comments.

      hours = selected_location_details["#{prefix}Hours"]
      return localized_hours(hours) if hours

This is also done here in PR #6624.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, if I make those changes here before #6624 is merged, then it would break the page rendering for "Ready to verify"? 🤔

I guess I can plug the same changes noted previously into this code:

selected_location_details: {
'name' => 'BALTIMORE — Post Office™',
'streetAddress' => '900 E FAYETTE ST RM 118',
'city' => 'BALTIMORE',
'state' => 'MD',
'zip5' => '21233',
'zip4' => '9715',
'phone' => '555-123-6409',
'hours' => [
{
'weekdayHours' => '8:30 AM - 4:30 PM',
},
{
'saturdayHours' => '9:00 AM - 12:00 PM',
},
{
'sundayHours' => 'Closed',
},
],
},

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomas-nava Yeah, there's a lot from #6624 that I'd have to pull into this branch to be able to use the updated selected location shape. Could we merge this without those and update #6624? Or, conversely, merge #6624 and I'll update this one?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with merging this first and updating in #6624 @aduth

selected_location_details['hours'].each do |hours_candidate|
hours = hours_candidate["#{prefix}Hours"]
Expand All @@ -40,7 +28,6 @@ def needs_proof_of_address?
private

attr_reader :enrollment
delegate :enrollment_code, to: :enrollment

def barcode_image_data
Barby::Code128C.new(enrollment_code).to_png(margin: 0, xdim: 2)
Expand Down
21 changes: 7 additions & 14 deletions app/views/idv/in_person/ready_to_verify/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<% end %>

<%= render AlertComponent.new(class: 'margin-y-4', text_tag: :div) do %>
<p class="font-sans-md margin-bottom-1"><strong><%= t('in_person_proofing.body.barcode.deadline', deadline: @presenter.formatted_due_date) %></strong></p>
<p class="margin-bottom-1"><strong><%= t('in_person_proofing.body.barcode.deadline', deadline: @presenter.formatted_due_date) %></strong></p>
<p class="margin-bottom-0"><%= t('in_person_proofing.body.barcode.deadline_restart') %></p>
<% end %>

Expand All @@ -31,18 +31,11 @@
<%= render ProcessListComponent.new(heading_level: :h3, class: 'margin-y-3') do |c| %>
<% c.item(heading: t('in_person_proofing.process.barcode.heading')) do %>
<p><%= t('in_person_proofing.process.barcode.info') %></p>
<figure class="display-inline-block margin-0">
<%= image_tag(
@presenter.barcode_data_url,
skip_pipeline: true,
alt: t('in_person_proofing.process.barcode.image_alt'),
class: 'display-block margin-bottom-1',
) %>
<figcaption class="text-center">
<span class="usa-sr-only"><%= t('in_person_proofing.process.barcode.caption_label') %>:</span>
<%= @presenter.formatted_enrollment_code %>
</figcaption>
</figure>
<%= render BarcodeComponent.new(
barcode_data: @presenter.enrollment_code,
label: t('in_person_proofing.process.barcode.caption_label'),
label_formatter: Idv::InPerson::EnrollmentCodeFormatter.method(:format),
) %>
<% end %>
<% c.item(heading: t('in_person_proofing.process.state_id.heading')) do %>
<p class="margin-bottom-105"><%= t('in_person_proofing.process.state_id.info') %></p>
Expand Down Expand Up @@ -85,7 +78,7 @@
<%= @presenter.selected_location_details['state'] %>
<%= @presenter.selected_location_details['zip5'] %>-<%= @presenter.selected_location_details['zip4'] %>
</div>
<h3 class="font-sans-sm margin-y-0"><%= t('in_person_proofing.body.barcode.retail_hours') %></h3>
<h3 class="margin-y-0 h4"><%= t('in_person_proofing.body.barcode.retail_hours') %></h3>
<div class="margin-bottom-2">
<%= t('date.range', from: t('date.day_names')[0], to: t('date.day_names')[4]) %>: <%= @presenter.selected_location_hours(:weekday) %><br>
<%= t('date.day_names')[5] %>: <%= @presenter.selected_location_hours(:saturday) %><br>
Expand Down
102 changes: 102 additions & 0 deletions app/views/user_mailer/in_person_ready_to_verify.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<p>
<%= t('user_mailer.in_person_ready_to_verify.greeting', name: @first_name) %><br>
<%= t('user_mailer.in_person_ready_to_verify.intro') %>
</p>

<table class="info-alert margin-y-4">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to make the alert heading:

  • the same font size as the alert body copy
  • bolded

I think this is one way to introduce some consistency in style between the email and the Login page.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The font size difference is present in the Figma designs (18px for bolded heading, 16px for unbolded text). It'd be easy enough for me to standardize them as the same body font size though if that's what we want.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see how it's a bit more noticeable a difference to control these font sizes in the email template though, since the default body font size is 13px in the emails (vs. 16px elsewhere in the app).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see how it's a bit more noticeable a difference to control these font sizes in the email template though, since the default body font size is 13px in the emails (vs. 16px elsewhere in the app).

This is a good point. Might be worth the design team looking at the email styling after pilot.

<tr>
<td width="16">
<%= image_tag('email/info.png', width: 16, height: 16, alt: '') %>
</td>
<td>
<p class="margin-bottom-1"><strong><%= t('in_person_proofing.body.barcode.deadline', deadline: @presenter.formatted_due_date) %></strong></p>
<p class="margin-bottom-0"><%= t('in_person_proofing.body.barcode.deadline_restart') %></p>
</td>
</tr>
</table>

<div class="border-1px border-primary-light radius-lg padding-4">
<h2 class="margin-top-0 margin-bottom-2 font-heading-lg text-bold">
<%= t('in_person_proofing.body.barcode.items_to_bring') %>
</h2>
<p><%= t('in_person_proofing.body.barcode.emailed_info') %></p>
<table class="process-list">
<tr>
<td><div class="process-list__circle">1</div></td>
<td>
<h3 class="font-heading-md text-bold"><%= t('in_person_proofing.process.barcode.heading') %></h3>
<p><%= t('in_person_proofing.process.barcode.info') %></p>
<%= render BarcodeComponent.new(
barcode_data: @presenter.enrollment_code,
label: nil,
label_formatter: Idv::InPerson::EnrollmentCodeFormatter.method(:format),
) %>
</td>
</tr>
<tr>
<td><div class="process-list__circle">2</div></td>
<td>
<h3 class="font-heading-md text-bold"><%= t('in_person_proofing.process.state_id.heading') %></h3>
<p class="margin-bottom-105"><%= t('in_person_proofing.process.state_id.info') %></p>
<ul class="usa-list margin-y-105">
<% t('in_person_proofing.process.state_id.acceptable_documents').each do |document| %>
<li><%= document %></li>
<% end %>
</ul>
<p><%= t('in_person_proofing.process.state_id.no_other_documents') %></p>
</td>
</tr>
<% if @presenter.needs_proof_of_address? %>
<tr>
<td><div class="process-list__circle">4</div></td>
<td>
<h3 class="font-heading-md text-bold"><%= t('in_person_proofing.process.proof_of_address.heading') %></h3>
<p class="margin-bottom-105"><%= t('in_person_proofing.process.proof_of_address.info') %></p>
<ul class="usa-list margin-y-105">
<% t('in_person_proofing.process.proof_of_address.acceptable_proof').each do |proof| %>
<li><%= proof %></li>
<% end %>
</ul>
</td>
</tr>
<% end %>
</table>
<p class="margin-bottom-0">
<%= t('in_person_proofing.body.barcode.items_to_bring_questions') %>
<%= link_to(
t('in_person_proofing.body.barcode.learn_more'),
MarketingSite.help_center_article_url(
category: 'verify-your-identity',
article: 'how-to-verify-in-person',
),
) %>
</p>
</div>

Comment thread
aduth marked this conversation as resolved.
<% if @presenter.selected_location_details.present? %>
<div class="margin-y-4">
<h2 class="font-sans-md margin-bottom-1 text-normal text-bold"><%= @presenter.selected_location_details['name'] %></h2>
<div class="margin-bottom-1">
<%= @presenter.selected_location_details['streetAddress'] %><br>
<%= @presenter.selected_location_details['city'] %>,
<%= @presenter.selected_location_details['state'] %>
<%= @presenter.selected_location_details['zip5'] %>-<%= @presenter.selected_location_details['zip4'] %>
</div>
<div><strong><%= t('in_person_proofing.body.barcode.retail_hours') %></strong></div>
<div class="margin-bottom-2">
<%= t('date.range', from: t('date.day_names')[0], to: t('date.day_names')[4]) %>: <%= @presenter.selected_location_hours(:weekday) %><br>
<%= t('date.day_names')[5] %>: <%= @presenter.selected_location_hours(:saturday) %><br>
<%= t('date.day_names')[6] %>: <%= @presenter.selected_location_hours(:sunday) %>
</div>
<div>
<%= @presenter.selected_location_details[:phone] %>
</div>
</div>
<% end %>

<p><%= t('in_person_proofing.body.barcode.speak_to_associate') %></p>

<p>
<strong><%= t('in_person_proofing.body.barcode.deadline', deadline: @presenter.formatted_due_date) %></strong>
<%= t('in_person_proofing.body.barcode.no_appointment_required') %>
</p>
2 changes: 1 addition & 1 deletion config/initializers/secure_headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
}

config.action_dispatch.default_headers.merge!(
'X-Frame-Options' => 'DENY',
'X-Frame-Options' => IdentityConfig.store.rails_mailer_previews_enabled ? 'SAMEORIGIN' : 'DENY',
'X-XSS-Protection' => '1; mode=block',
'X-Download-Options' => 'noopen',
)
Expand Down
2 changes: 2 additions & 0 deletions config/locales/components/en.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
en:
components:
barcode:
table_label: Barcode
clipboard_button:
label: Copy
javascript_required:
Expand Down
2 changes: 2 additions & 0 deletions config/locales/components/es.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
es:
components:
barcode:
table_label: Código de barras
clipboard_button:
label: Copiar
javascript_required:
Expand Down
2 changes: 2 additions & 0 deletions config/locales/components/fr.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
fr:
components:
barcode:
table_label: Code-barres
clipboard_button:
label: Copier
javascript_required:
Expand Down
1 change: 0 additions & 1 deletion config/locales/in_person_proofing/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ en:
barcode:
caption_label: Enrollment code
heading: A copy of your barcode
image_alt: Barcode
info: Print or scan from your mobile device.
proof_of_address:
acceptable_proof:
Expand Down
1 change: 0 additions & 1 deletion config/locales/in_person_proofing/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ es:
barcode:
caption_label: Código de registro
heading: Una copia de su código de barras
image_alt: Código de barras
info: Imprima o escanee desde su dispositivo móvil.
proof_of_address:
acceptable_proof:
Expand Down
Loading