-
Notifications
You must be signed in to change notification settings - Fork 166
LG-12603 Verified & Pending States for Identity Verification #10823
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
924fcfa
2bbaf98
12a2cd9
b9f48ba
b71c2d7
69aa186
82a2279
b0a5ddf
2e80998
5042830
aea44c7
8d29181
96e0224
e2fd0c3
1fece4b
32e99f4
cfd4dd6
c80768f
465664f
2385c14
4711176
9550923
fd8319b
1db9236
3216310
c95f6f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 |
| 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; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| import '@18f/identity-badge-tooltip/badge-tooltip-element'; |
| 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(''); | ||
| }); | ||
| }); |
| 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()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
| 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" | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? | ||
|
|
@@ -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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? | ||
|
|
@@ -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? | ||
|
|
||
|
|
||
| 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 %> |
| 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 %> |
| 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 %> |
| 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 %> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.border-warningalready 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-warningon the HTML elements and avoid this additional CSS altogether.