Skip to content
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

feat: [#5777] Add navigation buttons for steps in case contact form #5923

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
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
a6e65f9
[#5777] Update lock with updates from bin/setup and bundle
priyapower Jul 15, 2024
c81874d
[#5777] Add prototype buttons with form step navigation
priyapower Jul 15, 2024
38c95b7
[#5777] Update with appropriate icons
priyapower Jul 15, 2024
146fa54
Move both buttons to left of "Step"
priyapower Jul 15, 2024
404c4f2
[#5777] Add accessibility labels
priyapower Jul 15, 2024
dfd5408
[#5777] Ensure progress bar persists after validation
priyapower Jul 16, 2024
2e4fdce
[#5777] Refactor step navigation into unique component
priyapower Jul 16, 2024
31f6059
[#5777] Solve bug around validation when navigating backwards
priyapower Jul 17, 2024
e9ae652
[#5777] Remove debug point
priyapower Jul 17, 2024
5160d38
[#5777] Fix missing instantiated object every now and then
priyapower Jul 17, 2024
8b76177
[#5777] Remove excess behavior
priyapower Jul 17, 2024
1bea453
[#5777] :broom:
priyapower Jul 17, 2024
77309c9
[#5777] :broom:
priyapower Jul 17, 2024
2308e72
[#5777] Remove leave url for back navigation on first step until guid…
priyapower Jul 17, 2024
11fba79
[#5777] Add spec for step navigation component
priyapower Jul 17, 2024
032207d
[#5777] Update existing spec after modifying component
priyapower Jul 17, 2024
f6e0073
[#5777] :broom:
priyapower Jul 17, 2024
5d249ac
[#5777] Refactor large method
priyapower Jul 17, 2024
1f980b5
[#5777] Reset gemfile lock
priyapower Jul 22, 2024
06f41e0
:broom:
priyapower Jul 22, 2024
c665b64
:broom:
priyapower Jul 22, 2024
aef0fdf
Test
priyapower Jul 22, 2024
c1ebc07
Fix
priyapower Jul 22, 2024
1f09e29
Revert
priyapower Jul 22, 2024
744d82f
[#5777] Refactor with slot
priyapower Jul 22, 2024
ee3feb3
:broom:
priyapower Jul 22, 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
34 changes: 34 additions & 0 deletions app/components/form/step_navigation_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<div>
Copy link
Author

Choose a reason for hiding this comment

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

new component for step navigation via buttons that resemble carousel designs (chevron buttons)

<%# Back navigation %>
<%= button_tag type: :submit,
name: :nav_step,
value: @nav_back,
class: "btn btn-link #{@nav_back.nil? ? 'disabled' : 'enabled'}",
title: "Back step",
aria: { label: "Back step" },
Copy link
Contributor

Choose a reason for hiding this comment

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

Thank you for included aria attributes!

Suggested change
aria: { label: "Back step" },
aria: { label: "Back step", disabled: !@nav_back },

disabled: !@nav_back do %>
<% if @nav_back.present? %>
<%= link_to @nav_back, title: "Back step", aria: { label: "Back step" } do %>
Copy link
Author

Choose a reason for hiding this comment

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

Chose to do a link within the button to have access to visible display of route/url on hover

<i class="lni lni-chevron-left" alt="Chevron icon left"></i>
<% end %>
<% elsif %>
<i class="lni lni-chevron-left" alt="Chevron icon left"></i>
<% end %>
Comment on lines +10 to +16
Copy link
Contributor

Choose a reason for hiding this comment

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

disabled attribute above should disable the link, if so this would do:

Suggested change
<% if @nav_back.present? %>
<%= link_to @nav_back, title: "Back step", aria: { label: "Back step" } do %>
<i class="lni lni-chevron-left" alt="Chevron icon left"></i>
<% end %>
<% elsif %>
<i class="lni lni-chevron-left" alt="Chevron icon left"></i>
<% end %>
<%= link_to "#{@nav_back || '#'}", title: "Back step", aria: { label: "Back step" } do %>
<i class="lni lni-chevron-left" alt="Chevron icon left"></i>
<% end %>

<% end %>
<%# Next navigation %>
<%= button_tag type: :submit,
name: :nav_step,
value: @nav_forward,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
value: @nav_forward,
value: @nav_next,

right?

maybe back_path next_path are more descriptive names?

class: "btn btn-link #{@nav_next.nil? ? 'disabled' : 'enabled'}",
title: "Next step",
aria: { label: "Next step" },
disabled: !@nav_next do %>
<% if @nav_next.present? %>
<%= link_to @nav_next, title: "Next step", aria: { label: "Next step" } do %>
<i class="lni lni-chevron-right" alt="Chevron icon right"></i>
<% end %>
<% elsif %>
<i class="lni lni-chevron-right" alt="Chevron icon right"></i>
<% end %>
<% end %>
</div>
8 changes: 8 additions & 0 deletions app/components/form/step_navigation_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

class Form::StepNavigationComponent < ViewComponent::Base
Copy link
Author

Choose a reason for hiding this comment

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

New component

def initialize(nav_back: nil, nav_next: nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
def initialize(nav_back: nil, nav_next: nil)
def initialize(nav_back:, nav_next:)

I don't think nil defaults make sense for most cases? This would allow passing nil in explicitly if needed, but remind devs they need to specify the actions if they've forgotten!

@nav_back = nav_back
@nav_next = nav_next
end
end
17 changes: 10 additions & 7 deletions app/components/form/title_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@
<h2 class="col-12 col-md-6 case-contacts-form-subtitle"><%= @subtitle %></h2>
<% if @progress %>
<div class="col-12 col-md-6 align-items-center d-flex gap-2 mt-2 mt-md-0">
<p class="col-auto">
<%= @steps_in_text %>
</p>
<div class="col">
<div class="progress">
<div class="progress-bar primary-bg" role="progressbar" style="width: <%= @progress %>%" aria-valuenow="<%= @progress %>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<% if navigable %>
<%= navigable %>
<% end %>
<p class="col-auto">
<%= @steps_in_text %>
</p>
<div class="col">
<div class="progress">
<div class="progress-bar primary-bg" role="progressbar" style="width: <%= @progress %>%" aria-valuenow="<%= @progress %>" aria-valuemin="0" aria-valuemax="100"></div>
Comment on lines +17 to +22
Copy link
Author

Choose a reason for hiding this comment

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

No change - just indent

</div>
</div>
</div>
<% end %>
</div>

Expand Down
3 changes: 3 additions & 0 deletions app/components/form/title_component.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# frozen_string_literal: true

class Form::TitleComponent < ViewComponent::Base
# `Form::StepNavigationComponent` is defined in another file, so we can refer to it by class name.
renders_one :navigable, Form::StepNavigationComponent

def initialize(title:, subtitle:, step: nil, total_steps: nil, notes: nil, autosave: false)
@title = title
@subtitle = subtitle
Expand Down
32 changes: 21 additions & 11 deletions app/controllers/case_contacts/form_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,21 @@ class CaseContacts::FormController < ApplicationController

# wizard_path
def show
authorize @case_contact
manage_form_step
Copy link
Author

Choose a reason for hiding this comment

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

Use helper for repeated behavior

Copy link
Contributor

Choose a reason for hiding this comment

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

Leaving the authorize call in the action is better. Whether current_user is allowed to show/edit/update records is unrelated to the form step (mostly), and I want to see that a record has been authorized for this action, in the common pattern. Moving it to set_case_contact would make sense, but even that is too DRY for me, just leave it here.

get_cases_and_contact_types
@page = wizard_steps.index(step) + 1
@total_pages = steps.count

render_wizard
wizard_path
end

def update
authorize @case_contact
manage_form_step(step_navigation: true)
Copy link
Author

Choose a reason for hiding this comment

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

Use helper for repeated behavior

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a little confused what is happening here, seems like a lot of side effects are possible. also not clear how it interacts with some before_actions, which were already kind of a mess... does it make sense to check for steps in set_steps for example?

params[:case_contact][:status] = step.to_s if !@case_contact.active?
remove_unwanted_contact_types
remove_nil_draft_ids

if CaseContactUpdateService.new(@case_contact).update_attrs(case_contact_params)
respond_to do |format|
format.html {
if step == steps.last
finish_editing
else
render_wizard @case_contact, {}, {case_contact_id: @case_contact.id}
end
manage_navigation
Copy link
Author

Choose a reason for hiding this comment

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

New navigation behavior necessary for validating then routing to appropriate step

Copy link
Contributor

@thejonroberts thejonroberts Aug 26, 2024

Choose a reason for hiding this comment

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

I don't like this because it doesn't look like a typical controller action, I'd like to see the response behavior in this method, instead of finding out what happens in manage_navigation...

What do you think of changing to smaller methods that are only responsible for setting variables, and leaving all the render/redirects logic in the action methods (show/update)?

}
format.json { head :ok }
end
Expand All @@ -52,6 +44,13 @@ def set_case_contact
@case_contact = CaseContact.find(params[:case_contact_id])
end

def manage_form_step(step_navigation: false)
authorize @case_contact
@page = wizard_steps.index(step) + 1
@total_pages = steps.count
Comment on lines +49 to +50
Copy link
Author

Choose a reason for hiding this comment

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

This information is necessary for #update if you want to continue seeing the progress bar and navigation buttons after a form/validation error

Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't depend on any specific info, does it make more sense in a before action?

@nav_step = params[:nav_step] if step_navigation
Copy link
Author

Choose a reason for hiding this comment

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

New value

end

def get_cases_and_contact_types
@casa_cases = policy_scope(current_organization.casa_cases)
@casa_cases = @casa_cases.where(id: @case_contact.casa_case_id) if @case_contact.active?
Expand All @@ -65,6 +64,17 @@ def get_cases_and_contact_types
@selected_contact_type_ids = @case_contact.contact_type_ids
end

def manage_navigation
if @nav_step.present?
jump_to(@nav_step.split("/").last.to_sym)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm confused what @nav_step is... why not make it correspond to a wizard step? Is it pulling double duty as a step and as a path?

render_wizard @case_contact, {}, {case_contact_id: @case_contact.id}
Comment on lines +69 to +70
Copy link
Author

Choose a reason for hiding this comment

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

Used wicked jump_to and then renders wizard to go to the specific step from navigation buttons

elsif step == steps.last
finish_editing
else
render_wizard @case_contact, {}, {case_contact_id: @case_contact.id}
end
end

def finish_editing
message = ""
send_reimbursement_email(@case_contact)
Expand Down
5 changes: 3 additions & 2 deletions app/views/case_contacts/form/details.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<%= render(Form::TitleComponent.new(title: @case_contact.decorate.form_title, subtitle: "Contact details", step: @page, total_steps: @total_pages)) %>

<div>
<%= form_with(model: @case_contact, url: wizard_path(nil, case_contact_id: @case_contact.id), local: true, id: "casa-contact-form", class: "component-validated-form") do |form| %>
<%= render(Form::TitleComponent.new(title: @case_contact.decorate.form_title, subtitle: "Contact details", step: @page, total_steps: @total_pages)) do |component| %>
<% component.with_navigable(nav_back: nil, nav_next: next_wizard_path) %>
<% end %>
<%= render "/shared/error_messages", resource: @case_contact %>

<div class="card-style-1 pl-25 mb-10">
Expand Down
2 changes: 1 addition & 1 deletion app/views/case_contacts/form/expenses.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<%= render(Form::TitleComponent.new(title: @case_contact.decorate.form_title, subtitle: "Contact expenses", step: @page, total_steps: @total_pages)) %>

<div>
<%= form_with(model: @case_contact, url: wizard_path(nil, case_contact_id: @case_contact.id), local: true, id: "casa-contact-form", class: "component-validated-form") do |form| %>
<%= render(Form::TitleComponent.new(title: @case_contact.decorate.form_title, subtitle: "Contact expenses", step: @page, total_steps: @total_pages, navigable: Form::StepNavigationComponent.new(nav_back: previous_wizard_path, nav_next: nil))) %>
<%= render "/shared/error_messages", resource: @case_contact %>

<div class="card-style-1 pl-25 mb-10 pr-50">
Expand Down
2 changes: 1 addition & 1 deletion app/views/case_contacts/form/notes.html.erb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<div data-controller="autosave" data-autosave-good-alert-class="text-muted" data-autosave-bad-alert-class="text-danger">
<%= render(Form::TitleComponent.new(title: @case_contact.decorate.form_title, subtitle: "Contact notes", step: @page, total_steps: @total_pages, notes: @case_contact.decorate.form_page_notes[:notes], autosave: true)) %>

<div>
<%= form_with(model: @case_contact, url: wizard_path(nil, case_contact_id: @case_contact.id), id: "casa-contact-form", class: "component-validated-form", data: { "turbo-action": "advance", "autosave-target": "form" }) do |form| %>
<%= render(Form::TitleComponent.new(title: @case_contact.decorate.form_title, subtitle: "Contact notes", step: @page, total_steps: @total_pages, notes: @case_contact.decorate.form_page_notes[:notes], autosave: true, navigable: Form::StepNavigationComponent.new(nav_back: previous_wizard_path, nav_next: @case_contact.casa_org_any_expenses_enabled? ? next_wizard_path : nil))) %>
<%= render "contact_topic_notes", form: %>
<div class="card-style-1 pl-25 pr-25 mb-10">
<div class="d-flex justify-content-between mb-3"
Expand Down
19 changes: 19 additions & 0 deletions spec/components/form/step_navigation_component_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe Form::StepNavigationComponent, type: :component do
Copy link
Author

Choose a reason for hiding this comment

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

Testing new component for ability to enable or disable the buttons

context "can handle enabled button" do
it "enables button if value exists" do
render_inline(described_class.new(nav_back: "/details"))
expect(page).to have_selector(:link_or_button, "Back step")
end
end

context "can handle disabled button" do
it "disables buttons if value is nil" do
render_inline(described_class.new)
expect(page).not_to have_selector(:link_or_button, "Next step")
end
end
end
1 change: 1 addition & 0 deletions spec/components/form/title_component_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
let(:total_steps) { nil }
let(:notes) { nil }
let(:autosave) { nil }
let(:navigable) { nil }

before(:each) do
render_inline(described_class.new(title: title, subtitle: subtitle, step: step, total_steps: total_steps, notes: notes, autosave: autosave))
Expand Down
Loading