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

Conversation

priyapower
Copy link

@priyapower priyapower commented Jul 17, 2024

What github issue is this PR for, if any?

Resolves #5777

πŸ‘€ - This is my first contribution to this codebase, I am happy to make updates/change requests or discuss why I made certain decisions

❓ Open questions for a future PR:

  • "back" from the first step
    • CURRENT BEHAVIOR: disabled
    • Are you allowed to do this or should it be disabled?
    • If yes, should it align with the expected validation behavior as the "Back" button at bottom of form?
  • "forward" from last step
    • CURRENT BEHAVIOR: disabled
    • Are you allowed to do this or should it be disabled?
    • If you are allowed to "step forward", then would this just act as a secondary "submit" button?
  • Noticed a small style discrepancy that is out of scope for this work, but figured I should document - the dropdown is missing the padding on the right that other dropdowns have on this form
    Screen Shot 2024-07-17 at 3 51 41 PM
  • Noticed a possible bug with validation on the field "select all contact types"
    • If you validate step 1 (details) successfully, but then go back and remove the field value, you can still validate successfully. I wonder if it is catching an empty array/list or something because it's multiselect within the dropdown (design question: why isn't this just a required checkbox set... why a dropdown at all?)
    • #5777Bug

What changed, and why?

  • Added step buttons according to issue details (similar to carousel design and to the left of the phrase "step n of x"
  • πŸ› Fixed a bug where after validation errors, the progress and step portion of the title disappeared because we didn't persist the data from #update
  • Handles validation/saving between pages when using step buttons (excluding details outlined in "Questions for a future PR" above)

How is this tested? (please write tests!) πŸ’–πŸ’ͺ

Note: if you see a flake in your test build in github actions, please post in slack #casa "Flaky test: " :) πŸ’ͺ
Note: We love capybara tests! If you are writing both haml/js and ruby, please try to test your work with tests at every level including system tests like https://github.com/rubyforgood/casa/tree/main/spec/system

  • Added a new spec for the new step button component spec/components/form/step_navigation_component_spec.rb

Screenshots please :)

Run your local server and take a screenshot of your work! Try to include the URL of the page as well as the contents of the page.
#5777

Feelings gif (optional)

What gif best describes your feeling working on this issue? https://giphy.com/
How to embed:

scrubs_pound_it

@github-actions github-actions bot added dependencies Pull requests that update a dependency file ruby Pull requests that update Ruby code erb labels Jul 17, 2024
@priyapower priyapower force-pushed the priyapower/ISSUE-#5777/add-nav-buttons-to-wizard-form-contact branch from 370cd63 to 8b76177 Compare July 17, 2024 19:59
@priyapower priyapower marked this pull request as ready for review July 17, 2024 21:10
@priyapower priyapower changed the title [#5777] Add navigation buttons for steps in case contact form feat: [#5777] Add navigation buttons for steps in case contact form Jul 17, 2024
Gemfile.lock Outdated
@@ -570,9 +568,9 @@ GEM
PLATFORMS
arm64-darwin-21
ruby
x86_64-darwin-19
Copy link
Author

Choose a reason for hiding this comment

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

Happy to trash this gemfile lock change, but figured I would flag that my recent dev setup triggered these changes

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes you should drop this.

@@ -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)

aria: { label: "Back step" },
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

@@ -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

Comment on lines 14 to 16
<% if @navigable %>
<%= render(@navigable) %>
<% end %>
Copy link
Author

Choose a reason for hiding this comment

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

Optional navigation buttons

Comment on lines +17 to +22
<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>
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

@@ -1,11 +1,12 @@
# frozen_string_literal: true

class Form::TitleComponent < ViewComponent::Base
def initialize(title:, subtitle:, step: nil, total_steps: nil, notes: nil, autosave: false)
def initialize(title:, subtitle:, step: nil, total_steps: nil, notes: nil, autosave: false, navigable: nil)
Copy link
Author

Choose a reason for hiding this comment

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

new optional param for navigating through form via buttons near progress bar

Copy link
Collaborator

Choose a reason for hiding this comment

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

This ends up with suuuuper long call to render method. How about we just use a slot? https://viewcomponent.org/guide/slots.html#component-slots

@@ -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.

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?

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)?

Comment on lines +49 to +50
@page = wizard_steps.index(step) + 1
@total_pages = steps.count
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?

authorize @case_contact
@page = wizard_steps.index(step) + 1
@total_pages = steps.count
@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

Comment on lines +69 to +70
jump_to(@nav_step.split("/").last.to_sym)
render_wizard @case_contact, {}, {case_contact_id: @case_contact.id}
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

<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, navigable: Form::StepNavigationComponent.new(nav_back: nil, nav_next: next_wizard_path))) %>
Copy link
Author

Choose a reason for hiding this comment

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

Moved render under form so submit can catch

Added new parameter for navigable


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

Copy link
Collaborator

@elasticspoon elasticspoon left a comment

Choose a reason for hiding this comment

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

Haven't looked to close at the code yet but briefly testing it I seem to be able to proceed forward from the first page via arrow buttons and it ignores validations.

Peek 2024-07-19 11-34

That is I can proceed via arrow key but not submit button.

@elasticspoon
Copy link
Collaborator

* "back" from the first step
  
  * CURRENT BEHAVIOR: disabled
  * Are you allowed to do this or should it be disabled?
  * If yes, should it align with the expected validation behavior as the "Back" button at bottom of form?

Disabled makes sense.

* "forward" from last step
  
  * CURRENT BEHAVIOR: disabled
  * Are you allowed to do this or should it be disabled?
  * If you are allowed to "step forward", then would this just act as a secondary "submit" button?

Disabled makes sense.

* Noticed a small style discrepancy that is out of scope for this work, but figured I should document - the dropdown is missing the padding on the right that other dropdowns have on this form
  ![Screen Shot 2024-07-17 at 3 51 41 PM](https://private-user-images.githubusercontent.com/49959312/349678525-3d2edd91-00d7-4924-ba53-64386ab10537.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjE0MDM2NzUsIm5iZiI6MTcyMTQwMzM3NSwicGF0aCI6Ii80OTk1OTMxMi8zNDk2Nzg1MjUtM2QyZWRkOTEtMDBkNy00OTI0LWJhNTMtNjQzODZhYjEwNTM3LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MTklMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzE5VDE1MzYxNVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTMzMDIxZmZkMWI0OGI4ZGVhNWNmOTkxNDU4ODVjMGZhNzM4ZTc1NGRkZTA5NWRmYTVmN2ZmYWI1YjMyOTVmNjAmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.ggQFD40Ja71fkr-IIpmxh0suJzMbNW0z1Z-BqFd0hLs)

Good call. Feel free to open an issue πŸ™‚

* Noticed a possible bug with validation on the field "select all contact types"
  
  * If you validate step 1 (details) successfully, but then go back and remove the field value, you can still validate successfully. I wonder if it is catching an empty array/list or something because it's multiselect within the dropdown (design question: why isn't this just a required checkbox set... why a dropdown at all?)

Good catch. I make a note and open an issue later. Or you can if you would like. And for context the previous design was multiple checkboxes but we got some feedback that there are too many (20+) in some orgs.

@priyapower
Copy link
Author

Haven't looked to close at the code yet but briefly testing it I seem to be able to proceed forward from the first page via arrow buttons and it ignores validations.

Peek 2024-07-19 11-34 Peek 2024-07-19 11-34

That is I can proceed via arrow key but not submit button.

@elasticspoon

  1. I will remove gemfile.lock change
  2. I will make the slot update
  3. I will start looking into the conditional bug for validation and submit... I tested that exact case so I will need a second to dig in (#whydoesitworkonmine πŸ˜†)

@priyapower
Copy link
Author

@elasticspoon

  • βœ… I will remove gemfile.lock change
  • βœ… I will make the slot update
  • ❌ I will start looking into the conditional bug for validation and submit... I tested that exact case so I will need a second to dig in (#whydoesitworkonmine πŸ˜†)
    • I am unable to recreate "failed to validate on nav button" so I am still digging in here to figure it out why your local was able to click forward and avoid validation but mine catches validation. I'm going to try other test users and dig in further into the current forms code to see if I can spot why this behavior would have hit the exception for validation instead of the default of assuming validate on wizard movement
    • Do you mind confirming again if this behavior is still live on this branch? Which dev/test user are you using?
  • ❌ New spec failures are due to the slot change and updating the spec to reflect this new behavior - working on this update as well

Copy link
Contributor

@thejonroberts thejonroberts left a comment

Choose a reason for hiding this comment

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

I am a bit confused. This controller was a mess before you got to it, so it makes all of this a little harder to reason through, not your fault! I also need to learn more about ViewComponents, but all of that side looks really good.

I think my major concerns are the indirection of manage_form_step and the lack of clarity of what @nav_step represents.

I may take a crack at refactoring this controller to clear it up and use more wizard helpers from the gem.

Let me know if you have any questions about my reviews!

# frozen_string_literal: true

class Form::StepNavigationComponent < ViewComponent::Base
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!

@@ -9,29 +9,21 @@ class CaseContacts::FormController < ApplicationController

# wizard_path
def show
authorize @case_contact
manage_form_step
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.

Comment on lines +10 to +16
<% 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 %>
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 %>

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 },

<%# 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?

render_wizard
wizard_path
end

def update
authorize @case_contact
manage_form_step(step_navigation: true)
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?

else
render_wizard @case_contact, {}, {case_contact_id: @case_contact.id}
end
manage_navigation
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)?

Comment on lines +49 to +50
@page = wizard_steps.index(step) + 1
@total_pages = steps.count
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?

@@ -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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file erb ruby Pull requests that update Ruby code Tests! πŸŽ‰πŸ’–πŸ‘
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Back and Forth Buttons to Case Contact Form
3 participants