Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
924fcfa
changelog: User-Facing Improvements, identity verification, Verified …
kevinsmaster5 Jun 11, 2024
2bbaf98
add badges pertaining to verification status
kevinsmaster5 Jun 12, 2024
12a2cd9
add tooltip enabled component
kevinsmaster5 Jun 14, 2024
b9f48ba
add biometric verification to verified account view
kevinsmaster5 Jun 14, 2024
b71c2d7
add translated text
kevinsmaster5 Jun 14, 2024
69aa186
remove redundant key
kevinsmaster5 Jun 14, 2024
82a2279
change tooltip to use mouseover
kevinsmaster5 Jun 14, 2024
b0a5ddf
clarify tooltip js and add testing
kevinsmaster5 Jun 17, 2024
2e80998
add spec for badge tooltip
kevinsmaster5 Jun 17, 2024
5042830
move styling to badge tooltip
kevinsmaster5 Jun 17, 2024
aea44c7
add readme to package
kevinsmaster5 Jun 17, 2024
8d29181
sync application.yml.default for missing keys
kevinsmaster5 Jun 17, 2024
96e0224
updated yarn.lock
kevinsmaster5 Jun 17, 2024
e2fd0c3
fix errors in yml
kevinsmaster5 Jun 17, 2024
1fece4b
normalize yml
kevinsmaster5 Jun 17, 2024
32e99f4
post yarn install
kevinsmaster5 Jun 17, 2024
cfd4dd6
pinpoint countries
kevinsmaster5 Jun 18, 2024
c80768f
yarn... lock
kevinsmaster5 Jun 18, 2024
465664f
re-normalize yaml country codes
kevinsmaster5 Jun 18, 2024
2385c14
fix lint errors in badge scss's
kevinsmaster5 Jun 18, 2024
4711176
remove unused keys
kevinsmaster5 Jun 18, 2024
9550923
replace missing translation key
kevinsmaster5 Jun 18, 2024
fd8319b
remove hardcoded app_name from translations
kevinsmaster5 Jun 18, 2024
1db9236
fix inconsistencies in verified button styling
kevinsmaster5 Jun 18, 2024
3216310
remove unneeded css
kevinsmaster5 Jun 20, 2024
c95f6f3
add view tests for new badge placement
kevinsmaster5 Jun 20, 2024
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: 2 additions & 0 deletions app/components/badge_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ class BadgeComponent < BaseComponent
ICONS = %i[
lock
check_circle
warning
info
].to_set.freeze

attr_reader :icon, :tag_options
Expand Down
14 changes: 14 additions & 0 deletions app/components/badge_component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,17 @@
.lg-verification-badge .usa-icon {
margin-right: units(1);
}

.border-warning {
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.

.border-warning already exists, as implemented by the design system, so I don't think we need to duplicate it here, and I wouldn't expect the utility class to customize icon or text color.

Instead, I think we could apply classes border-warning text-warning on the HTML elements and avoid this additional CSS altogether.

border-color: color('warning');
.usa-icon {
color: color('warning');
}
}

.border-info {
border-color: color('info');
.usa-icon {
color: color('info');
}
}
25 changes: 25 additions & 0 deletions app/components/badge_tooltip_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true

class BadgeTooltipComponent < BaseComponent
attr_reader :tag_options, :tooltip_text

def initialize(tooltip_text:, **tag_options)
@tag_options = tag_options
@tooltip_text = tooltip_text
end

def call
content_tag(
:'lg-badge-tooltip',
badge_content,
'tooltip-text': tooltip_text,
)
end

def badge_content
render BadgeComponent.new(
**tag_options,
type: :badge,
).with_content(content)
end
end
10 changes: 10 additions & 0 deletions app/components/badge_tooltip_component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@use 'uswds-core' as *;
@forward 'usa-tooltip';

// Workaround for tooltip issue preventing buttons from showing as full width at small screen sizes.
// See: https://github.com/uswds/uswds/issues/5273
.usa-tooltip {
@include at-media-max('mobile-lg') {
display: block;
}
}
1 change: 1 addition & 0 deletions app/components/badge_tooltip_component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@18f/identity-badge-tooltip/badge-tooltip-element';
23 changes: 23 additions & 0 deletions app/javascript/packages/badge-tooltip/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# `@18f/identity-badge-tooltip`

Custom element for a badge component.

## Usage

### Custom Element

Importing the element will register the `<lg-badge-tooltip>` custom element:

```ts
import '@18f/identity-badge-tooltip/badge-tooltip-element';
```

```html
<lg-badge-tooltip tooltip-text="Your identity has been verified.">
<span class="usa-tooltip">
<div class="lg-verification-badge usa-tooltip__trigger" data_position="top" type="badge" data-position="top" aria-describedby="tooltip-712979" tabindex="0">
<span class="icon usa-icon text-success" id="icon-b48becfc"></span>
</div>
</span>
</lg-badge-tooltip>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { getByText } from '@testing-library/dom';
import userEvent from '@testing-library/user-event';
import { computeAccessibleDescription } from 'dom-accessibility-api';
import './badge-tooltip-element';

describe('BadgeTooltipElement', () => {
function createAndConnectElement({ tooltipText = '' } = {}) {
const element = document.createElement('lg-badge-tooltip');
element.setAttribute('tooltip-text', tooltipText);
element.setAttribute('tooltip-text', 'Your identity has been verified');
element.innerHTML = '<div class="lg-verification-badge usa-tooltip">Verified</div>';
document.body.appendChild(element);
return element;
}

it('shows a tooltip when mouseover, until mouseout', async () => {
const element = createAndConnectElement();

const badge = getByText(element, 'Verified');

await userEvent.hover(badge);
expect(computeAccessibleDescription(badge)).to.be.equal('Your identity has been verified');

await userEvent.unhover(badge);
expect(computeAccessibleDescription(badge)).to.be.equal('');
});
});
65 changes: 65 additions & 0 deletions app/javascript/packages/badge-tooltip/badge-tooltip-element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { tooltip } from '@18f/identity-design-system';

class BadgeTooltipElement extends HTMLElement {
badge: HTMLElement;

connectedCallback() {
this.badge = this.querySelector('.usa-tooltip')!;

this.setUpTooltip();
this.badge.addEventListener('mouseover', () => this.handleHover());
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.

How does someone access the tooltip content if they're using a keyboard or screen reader?

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'm not sure, should those be tab-focusable? It seemed like from Kamal's comment they would only be a hover
https://www.figma.com/design/RbqHzdFNiCMTXSKSBOqwYX?node-id=4482-9726#769800201
It leaves Mobile out of the picture also.

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 way I would expect it to work is that the badge is focusable (which it is currently), and that the tooltip is visible as long as the badge has focus. In addition to the current hover behavior.

}

/**
* Retrieves the text to be shown in the tooltip.
*/
get tooltipText(): string {
return this.getAttribute('tooltip-text')!;
}

/**
* Initializes the tooltip element.
*/
setUpTooltip() {
const { tooltipBody } = tooltip.setup(this.badge);

// A default USWDS tooltip will always be visible when the badge is hovered over.
// To ensure the tooltip content is read when made visible,
// change its contents to a live region.
tooltipBody.setAttribute('aria-live', 'polite');
}

/**
* Handles the badge mouseover.
*/
handleHover() {
this.showTooltip();
}

/**
* Displays confirmation tooltip and binds event to dismiss tooltip on mouseout.
*/
showTooltip() {
const { trigger, body } = tooltip.getTooltipElements(this.badge);
body.textContent = this.tooltipText;
tooltip.show(body, trigger, 'top');

function hideTooltip() {
body.textContent = '';
tooltip.hide(body);
}
this.badge.addEventListener('mouseout', hideTooltip, { once: true });
}
}

declare global {
interface HTMLElementTagNameMap {
'lg-badge-tooltip': BadgeTooltipElement;
}
}

if (!customElements.get('lg-badge-tooltip')) {
customElements.define('lg-badge-tooltip', BadgeTooltipElement);
}

export default BadgeTooltipElement;
11 changes: 11 additions & 0 deletions app/javascript/packages/badge-tooltip/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "@18f/identity-badge-tooltip",
"version": "1.0.0",
"private": true,
"dependencies": {
"@18f/identity-design-system": "^9.2.0"
},
"sideEffects": [
"./badge-tooltip-element.ts"
]
}
38 changes: 35 additions & 3 deletions app/presenters/account_show_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ def show_password_reset_partial?
end

def show_pii_partial?
decrypted_pii.present? || user.identity_verified?
decrypted_pii.present? || user.identity_verified? unless
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.

Can we add test coverage for these changes?

user.pending_in_person_enrollment.present? || user.
gpo_verification_pending_profile.present?
end

def show_manage_personal_key_partial?
Expand All @@ -34,10 +36,36 @@ def show_gpo_partial?
user.gpo_verification_pending_profile?
end

def show_ipp_partial?
user.pending_in_person_enrollment.present?
end

def showing_any_partials?
show_password_reset_partial? ||
show_gpo_partial? || show_ipp_partial? || show_pii_partial?
end

def showing_alerts?
show_service_provider_continue_partial? ||
show_password_reset_partial? ||
show_gpo_partial?
show_password_reset_partial?
end

def show_unverified?
show_password_reset_partial? &&
(!show_ipp_partial? || !show_gpo_partial?)
end

def service_provider_or_app_name
if user.identities.count == 0
APP_NAME
else
user.identities.last.friendly_name
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.

Do we have any guarantees that this is the name of the service provider that the user was trying to access for identity verification, as opposed to another partner application that the user is authenticated with (at IAL1) ?

end
end

def formatted_due_date
user.pending_in_person_enrollment.due_date.
strftime(I18n.t('time.formats.event_date'))
end

def show_unphishable_badge?
Expand All @@ -60,6 +88,10 @@ def personal_key_generated_at
user.personal_key_generated_at
end

def biometric_identity_verification?
user.identity_verified_with_biometric_comparison?
end

def header_personalization
return decrypted_pii.first_name if decrypted_pii.present?

Expand Down
4 changes: 0 additions & 4 deletions app/views/accounts/_badges.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
<% if @presenter.show_unphishable_badge? %>
<%= render BadgeComponent.new(icon: :lock).with_content(t('headings.account.unphishable')) %>
<% end %>

<% if @presenter.show_verified_badge? %>
<%= render BadgeComponent.new(icon: :check_circle).with_content(t('headings.account.verified_account')) %>
<% end %>
2 changes: 1 addition & 1 deletion app/views/accounts/_pending_profile_gpo.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<%= render AlertComponent.new(type: :warning, text_tag: 'div') do %>
<%= render AlertComponent.new(type: :info, text_tag: 'div') do %>
<p>
<%= t('account.index.verification.instructions') %>
</p>
Expand Down
8 changes: 8 additions & 0 deletions app/views/accounts/_pending_profile_ipp.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<%= render AlertComponent.new(type: :info, text_tag: 'div') do %>
<p>
<%= t('account.index.verification.in_person_instructions', deadline: @presenter.formatted_due_date) %>
</p>
<p>
<%= link_to t('account.index.verification.show_bar_code'), idv_in_person_ready_to_verify_url %>
</p>
<% end %>
12 changes: 2 additions & 10 deletions app/views/accounts/_pii.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,8 @@
</div>
</div>
<% end %>
<div class="margin-bottom-4 card profile-info-box">
<div class="grid-row margin-bottom-1 margin-top-0">
<div class="grid-col-12">
<h2 class="margin-0">
<%= t('headings.account.verified_information') %>
<%= image_tag asset_url('lock.svg'), width: 8, height: 10 %>
</h2>
</div>
</div>


<div class="border-bottom border-primary-light">
<div class="padding-1 grid-row border-top border-left border-right border-primary-light">
<div class="tablet:grid-col-4 text-bold">
Expand Down Expand Up @@ -74,4 +67,3 @@
<%= link_to t('account.security.link'), MarketingSite.help_url %>
</div>
<% end %>
</div>
6 changes: 6 additions & 0 deletions app/views/accounts/_unverified_profile.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<%= render AlertComponent.new(type: :warning, text_tag: 'div') do %>
<p>
<%= t('account.index.verification.finish_verifying', partner_agency: @presenter.service_provider_or_app_name) %>
<br><%= link_to t('account.index.verification.learn_more_link'), MarketingSite.help_url %>
</p>
<% end %>
9 changes: 9 additions & 0 deletions app/views/accounts/_validated_badge.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<% if @presenter.show_verified_badge? %>
<%= render BadgeTooltipComponent.new(icon: :check_circle, tooltip_text: @presenter.biometric_identity_verification? ? t('components.badge_tooltip_component.verified_biometric') : t('components.badge_tooltip_component.verified'), class: 'usa-tooltip', data_position: 'top').with_content(t('headings.account.verified_account')) %>
<% end %>
<% if @presenter.show_unverified? %>
<%= render BadgeTooltipComponent.new(icon: :check_circle, tooltip_text: t('components.badge_tooltip_component.unverified'), class: 'usa-tooltip border-warning', data_position: 'top').with_content(t('headings.account.unverified')) %>
<% end %>
<% if @presenter.show_ipp_partial? || @presenter.show_gpo_partial? %>
<%= render BadgeTooltipComponent.new(icon: :check_circle, tooltip_text: t('components.badge_tooltip_component.pending'), class: 'usa-tooltip border-info', data_position: 'top').with_content(t('headings.account.pending')) %>
<% end %>
Loading