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
14 changes: 7 additions & 7 deletions app/components/block_link_component.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
class BlockLinkComponent < BaseComponent
attr_reader :url, :action, :new_tab, :tag_options
attr_reader :url, :action, :new_tab, :tag_options, :component

alias_method :new_tab?, :new_tab

def initialize(url:, action: tag.method(:a), new_tab: false, **tag_options)
@action = action
def initialize(url: '#', component: nil, new_tab: false, **tag_options)
@url = url
@component = component
@new_tab = new_tab
@tag_options = tag_options
end
Expand All @@ -21,11 +21,11 @@ def target
end

def wrapper(&block)
wrapper = action.call(**tag_options, href: url, class: css_class, target:, &block)
if wrapper.respond_to?(:render_in)
render wrapper, &block
if component
render component.new(href: url, class: css_class), &block
else
wrapper
action = tag.method(:a)
action.call(**tag_options, href: url, class: css_class, target:, &block)
end
end
end
3 changes: 3 additions & 0 deletions app/components/form_link_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<lg-form-link>
<%= link_to('#', **tag_options) { content } %>
</lg-form-link>
7 changes: 7 additions & 0 deletions app/components/form_link_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class FormLinkComponent < BaseComponent
attr_reader :tag_options

def initialize(**tag_options)
@tag_options = tag_options
end
end
1 change: 1 addition & 0 deletions app/components/form_link_component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@18f/identity-form-link/form-link-element';
22 changes: 22 additions & 0 deletions app/javascript/packages/form-link/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# `@18f/identity-form-link`

Custom element for links which submit as a form, supporting non-GET navigation.

## Usage

### Custom Element

Importing the element will register the `<lg-form-link>` custom element:

```ts
import '@18f/identity-form-link/form-link-element';
```

The custom element will implement the link submission behavior, but all markup must already exist.

```html
<lg-form-link>
<a href="https://example.com">Submit</a>
<form method="post" action="https://example.com" class="display-none"></form>
</lg-form-link>
```
28 changes: 28 additions & 0 deletions app/javascript/packages/form-link/form-link-element.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import sinon from 'sinon';
import { getByRole, fireEvent } from '@testing-library/dom';
import './form-link-element';

describe('FormLinkElement', () => {
function createElement() {
document.body.innerHTML = `
<lg-form-link>
<a href="https://example.com">Submit</a>
<form method="post" action="https://example.com" class="display-none"></form>
</lg-form-link>
`;

return document.body.querySelector('lg-form-link')!;
}

it('submits form on link click', () => {
const element = createElement();
const link = getByRole(element, 'link');

const onSubmit = sinon.stub();
element.form.submit = onSubmit;
const didPreventDefault = !fireEvent.click(link);

expect(onSubmit).to.have.been.called();
expect(didPreventDefault).to.be.true();
});
});
30 changes: 30 additions & 0 deletions app/javascript/packages/form-link/form-link-element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
class FormLinkElement extends HTMLElement {
connectedCallback() {
this.link.addEventListener('click', this.submit);
}

get form(): HTMLFormElement {
return this.querySelector('form')!;
}

get link(): HTMLAnchorElement {
return this.querySelector('a')!;
}

submit = (event: MouseEvent) => {
event.preventDefault();
this.form.submit();
};
}

declare global {
interface HTMLElementTagNameMap {
'lg-form-link': FormLinkElement;
}
}

if (!customElements.get('lg-form-link')) {
customElements.define('lg-form-link', FormLinkElement);
}

export default FormLinkElement;
8 changes: 8 additions & 0 deletions app/javascript/packages/form-link/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@18f/identity-form-link",
"version": "1.0.0",
"private": true,
"sideEffects": [
"./form-link-element.ts"
]
}
48 changes: 23 additions & 25 deletions app/views/sign_up/emails/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,29 @@
<%= t('notices.signed_up_but_unconfirmed.first_paragraph_end') %>
</p>

<div class="width-10">
<hr class="margin-y-4 border-width-05 border-info">
</div>
<p><%= t('devise.registrations.close_window') %></p>

<%= simple_form_for @resend_email_confirmation_form,
html: { class: 'margin-bottom-2' },
url: sign_up_register_path do |f| %>
<%= f.input :email, as: :hidden %>
<%= f.input :resend, as: :hidden %>
<p><%= t('notices.signed_up_but_unconfirmed.no_email_sent_explanation_start') %>
<%= f.button :button, t('links.resend'), class: 'usa-button--unstyled margin-left-05' %></p>

<p>
<%= t(
'notices.use_diff_email.text_html',
link_html: link_to(t('notices.use_diff_email.link'), sign_up_email_path),
) %>
</p>
<p><%= t('devise.registrations.close_window') %></p>
<%= render TroubleshootingOptionsComponent.new do |c| %>
<% c.with_header { t('components.troubleshooting_options.default_heading') } %>
<% c.with_option(component: FormLinkComponent) do %>
<%= t('notices.signed_up_but_unconfirmed.resend_confirmation_email') %>
<%= simple_form_for @resend_email_confirmation_form,
html: { class: 'display-none' },
url: sign_up_register_path do |f| %>
<%= f.input :email, as: :hidden %>
<%= f.input :resend, as: :hidden %>
<%= f.button :button, t('notices.signed_up_but_unconfirmed.resend_confirmation_email') %>
<% end %>
<% end %>
<% c.with_option(
url: sign_up_email_path,
).with_content(t('notices.use_diff_email.link').upcase_first) %>
<% end %>

<% if FeatureManagement.enable_load_testing_mode? && EmailAddress.find_with_email(email) %>
<%= link_to(
'CONFIRM NOW',
sign_up_create_email_confirmation_url(confirmation_token: EmailAddress.find_with_email(email).confirmation_token),
id: 'confirm-now',
) %>
<% end %>
<% if FeatureManagement.enable_load_testing_mode? && EmailAddress.find_with_email(email) %>
<%= link_to(
'CONFIRM NOW',
sign_up_create_email_confirmation_url(confirmation_token: EmailAddress.find_with_email(email).confirmation_token),
id: 'confirm-now',
) %>
<% end %>
2 changes: 1 addition & 1 deletion config/locales/notices/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ en:
first_paragraph_end: with a link to confirm your email address. Follow the link
to continue creating your account.
first_paragraph_start: We sent an email to
no_email_sent_explanation_start: Didn’t receive an email?
resend_confirmation_email: Resend the confirmation email
timeout_warning:
partially_signed_in:
continue: Continue sign in
Expand Down
2 changes: 1 addition & 1 deletion config/locales/notices/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ es:
first_paragraph_end: con un enlace para confirmar su email. Siga el enlace para
continuar creando su cuenta.
first_paragraph_start: Enviamos un email a
no_email_sent_explanation_start: '¿No recibió un email?'
resend_confirmation_email: Reenviar el correo electrónico de confirmación
timeout_warning:
partially_signed_in:
continue: Continuar el inicio de sesión
Expand Down
2 changes: 1 addition & 1 deletion config/locales/notices/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fr:
first_paragraph_end: avec un lien pour confirmer votre adresse courriel. Suivez
le lien pour continuer à créer votre compte.
first_paragraph_start: Nous avons envoyé un courriel à
no_email_sent_explanation_start: Vous n’avez pas reçu d’e-mail?
resend_confirmation_email: Renvoyer le courriel de confirmation
timeout_warning:
partially_signed_in:
continue: Continuer la connexion
Expand Down
30 changes: 13 additions & 17 deletions spec/components/block_link_component_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,22 @@
end
end

context 'with custom renderer' do
# rubocop:disable RSpec/LeakyConstantDeclaration
class ExampleBlockLinkCustomRendererComponent < BaseComponent
def initialize(href:, **)
@href = href
end

def call
content_tag(:button, "Example #{content.strip}", data: { href: @href })
end
context 'with a component' do
before do
stub_const(
'TestComponent', Class.new(BaseComponent) do
def call
content_tag(:div, 'from test component', class: 'style')
end
end
)
end
# rubocop:enable RSpec/LeakyConstantDeclaration

it 'renders using the custom renderer' do
rendered = render_inline BlockLinkComponent.new(
url: '/',
action: ExampleBlockLinkCustomRendererComponent.method(:new),
).with_content('Link Text')
it 'renders using the specified component' do
rendered = render_inline(BlockLinkComponent.new(component: TestComponent))

expect(rendered).to have_css('button[data-href="/"]', text: 'Example Link Text')
expect(rendered).to have_css('.style')
expect(rendered).to have_text('from test component')
end
end
end
14 changes: 14 additions & 0 deletions spec/components/form_link_component_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'rails_helper'

RSpec.describe FormLinkComponent, type: :component do
let(:options) { { href: '/', method: :post } }
let(:content) { 'Title' }

subject(:rendered) do
render_inline(described_class.new(**options).with_content(content))
end

it 'renders custom element with link' do
expect(rendered).to have_css('lg-form-link a[href="/"]', text: content)
end
end
2 changes: 1 addition & 1 deletion spec/features/visitors/email_confirmation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
it 'sends the confirmation email again' do
sign_up_with('test@example.com')

expect { click_on t('links.resend') }.
expect { click_on t('notices.signed_up_but_unconfirmed.resend_confirmation_email') }.
to change { ActionMailer::Base.deliveries.count }.by(1)

expect(last_email.html_part.body).to have_content(
Expand Down
2 changes: 1 addition & 1 deletion spec/support/features/session_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ def submit_form_with_valid_but_wrong_email
end

def click_link_to_use_a_different_email
click_link t('notices.use_diff_email.link')
click_link t('notices.use_diff_email.link').upcase_first
Comment thread
jc-gsa marked this conversation as resolved.
Outdated
end

def submit_form_with_valid_email(email = 'test@test.com')
Expand Down
8 changes: 6 additions & 2 deletions spec/views/sign_up/emails/show.html.erb_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@
expect(rendered).to have_selector('h1', text: t('headings.verify_email'))
end

it 'contains link to resend confirmation page' do
it 'contains a form link to resend confirmation page' do
render

expect(rendered).to have_button(t('links.resend'))
expect(rendered).to have_selector('lg-form-link')
expect(rendered).to have_link(href: '#', class: ['usa-link', 'block-link'])
expect(rendered).
to have_button(t('notices.signed_up_but_unconfirmed.resend_confirmation_email'))
expect(rendered).to have_css("form[action='#{sign_up_register_path}']")
end

context 'when enable_load_testing_mode? is true and email address found' do
Expand Down