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

Issue 6187 admin new case contacts #6215

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
14 changes: 14 additions & 0 deletions app/components/form/multiple_select_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
data-multiple-select-options-value="<%= @options %>"
data-multiple-select-selected-items-value="<%= @selected_items %>"
data-multiple-select-placeholder-term-value="<%= @placeholder_term %>"
data-multiple-select-show-all-option-value="<%= @show_all_option %>"
data-multiple-select-with-options-value="true">

<template data-multiple-select-target="option">
Expand All @@ -18,6 +19,19 @@
<div class="badge rounded-pill bg-primary active px-3">DATA_LABEL</div>
</template>

<%# not needed, but want to be explicit when including this markup %>
<% if @show_all_option %>
<template data-multiple-select-target="hiddenItem">
<div class="d-none"></div>
</template>

<template data-multiple-select-target="selectAllOption">
<div class="d-flex align-items-baseline">
<span data-test="select-all-input" class='mr-5'>DATA_LABEL</span>
</div>
</template>
<% end %>

<%= @form.select @name, {}, { multiple: true } , {
data: { "multiple-select-target": "select", } , class: "form-control-lg form-select form-select-lg input-group-lg"
} %>
Expand Down
3 changes: 2 additions & 1 deletion app/components/form/multiple_select_component.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# frozen_string_literal: true

class Form::MultipleSelectComponent < ViewComponent::Base
def initialize(form:, name:, options:, selected_items:, render_option_subtext: false, placeholder_term: nil)
def initialize(form:, name:, options:, selected_items:, render_option_subtext: false, placeholder_term: nil, show_all_option: false)
@form = form
@name = name
@options = options.to_json
@selected_items = selected_items
@render_option_subtext = render_option_subtext
@placeholder_term = placeholder_term
@show_all_option = show_all_option
end
end
56 changes: 48 additions & 8 deletions app/javascript/controllers/multiple_select_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import { Controller } from '@hotwired/stimulus'
import TomSelect from 'tom-select'

export default class extends Controller {
static targets = ['select', 'option', 'item']
static targets = ['select', 'option', 'item', 'hiddenItem', 'selectAllOption']
static values = {
options: Array,
selectedItems: Array,
withOptions: Boolean,
placeholderTerm: {
type: String,
default: 'contact(s)'
}
},
showAllOption: Boolean
}

connect () {
Expand All @@ -37,11 +38,44 @@ export default class extends Controller {
const itemTemplate = this.itemTarget.innerHTML
const placeholder = `Select or search ${this.placeholderTermValue}`

const showAllOptionCheck = this.showAllOptionValue
const hiddenItemTemplate = showAllOptionCheck && this.hiddenItemTarget && this.hiddenItemTarget.innerHTML
const showAllOptionTemplate = showAllOptionCheck && this.selectAllOptionTarget && this.selectAllOptionTarget.innerHTML

// orderedOptionVals is of type (" " | number)[] - the " " could appear
// because using it as the value for the select/unselect all option
let orderedOptionVals = this.optionsValue.map(opt => opt.value)
if (showAllOptionCheck) {
// using " " as value instead of "" bc tom-select doesn't init the "" in the item list
orderedOptionVals = [' '].concat(orderedOptionVals)
Copy link
Contributor Author

@alex-yi37 alex-yi37 Feb 18, 2025

Choose a reason for hiding this comment

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

Using ' ' (space character) here seems to be treated the same as '' (empty string) when form data gets parsed in the back end. Not sure how empty string values are getting stripped when associating contact type ids with a case, but seems to work as expected since I can create casa cases locally.

}

const hasInitialItems = Array.isArray(this.selectedItemsValue) && this.selectedItemsValue.length
// initItems: number[], possibly empty
let initItems = this.selectedItemsValue
if (showAllOptionCheck) {
const emptyItem = [' ']
initItems = hasInitialItems ? emptyItem.concat(this.selectedItemsValue) : orderedOptionVals
}

const dropdownOptions = showAllOptionCheck
? [{ text: 'Select/Unselect all', subtext: '', value: ' ', group: '' }].concat(this.optionsValue)
: this.optionsValue

/* eslint-disable no-new */
new TomSelect(this.selectTarget, {
onItemAdd: function () {
onItemRemove: function (value) {
if (value === ' ') {
this.clear()
}
},
onItemAdd: function (value) {
this.setTextboxValue('')
this.refreshOptions()

if (value === ' ') {
this.addItems(orderedOptionVals)
}
},
plugins: {
remove_button: {
Expand All @@ -54,19 +88,25 @@ export default class extends Controller {
uncheckedClassNames: ['form-check-input']
}
},
options: this.optionsValue,
items: this.selectedItemsValue,
options: dropdownOptions,
items: initItems,
placeholder,
hidePlaceholder: true,
searchField: ['text', 'group'],
render: {
option: function (data, escape) {
let html = optionTemplate.replace(/DATA_LABEL/g, escape(data.text))
html = html.replace(/DATA_SUB_TEXT/g, escape(data.subtext))
let html

if (showAllOptionCheck && data && data.value === ' ') {
html = showAllOptionTemplate.replace(/DATA_LABEL/g, escape(data.text))
} else {
html = optionTemplate.replace(/DATA_LABEL/g, escape(data.text))
html = html.replace(/DATA_SUB_TEXT/g, escape(data.subtext))
}
return html
},
item: function (data, escape) {
return itemTemplate.replace(/DATA_LABEL/g, escape(data.text))
return showAllOptionCheck && data.value === ' ' ? hiddenItemTemplate : itemTemplate.replace(/DATA_LABEL/g, escape(data.text))
}
}
})
Expand Down
3 changes: 2 additions & 1 deletion app/views/case_contacts/form/_contact_types.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
name: :contact_type_ids,
options: options.decorate.map { |ct| ct.hash_for_multi_select_with_cases(casa_cases&.pluck(:id)) },
selected_items: selected_items,
render_option_subtext: true
render_option_subtext: true,
show_all_option: true,
)) %>
</div>
34 changes: 30 additions & 4 deletions spec/system/casa_cases/edit_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
let(:contact_type_group) { create(:contact_type_group, casa_org: organization) }
let(:other_org_contact_type_group) { create(:contact_type_group, casa_org: other_organization) }
let!(:contact_type) { create(:contact_type, contact_type_group: contact_type_group) }
let!(:another_contact_type) { create(:contact_type, contact_type_group: contact_type_group) }
let!(:saved_case_contact_type) { create(:casa_case_contact_type, casa_case: casa_case, contact_type: contact_type) }
let!(:other_org_contact_type) { create(:contact_type, contact_type_group: other_org_contact_type_group) }
let!(:siblings_casa_cases) do
create(:casa_case, :with_one_court_order, casa_org: organization)
Expand Down Expand Up @@ -41,6 +43,20 @@
select "Submitted", from: "casa_case_court_report_status"

find(".ts-control").click

ts_checkboxes = page.all(".ts-dropdown-content input")

select_all_el = page.find("span[data-test=select-all-input]")
# uncheck all contact type options
select_all_el.click
ts_checkboxes.each do |el|
expect(el).not_to be_checked
end
# check all contact type options
select_all_el.click
expect(ts_checkboxes).to all(be_checked)

# unselect contact_type from dropdown
find("span", text: contact_type.name).click

page.find('button[data-action="court-order-form#add"]').click
Expand All @@ -57,7 +73,7 @@
expect(page).to have_text("Court Order Text One")
expect(page).not_to have_text("Deactivate Case")

expect(casa_case.contact_types).to eq [contact_type]
expect(casa_case.contact_types).to eq [another_contact_type]
has_checked_field? contact_type.name
end

Expand Down Expand Up @@ -222,9 +238,19 @@
expect(page).to have_text("Set Implementation Status")

find(".ts-control").click
find("span", text: "Youth").click

within ".actions-cc" do
ts_checkboxes = page.all(".ts-dropdown-content input")

select_all_el = page.find("span[data-test=select-all-input]")
# uncheck all contact type options
select_all_el.click
ts_checkboxes.each do |el|
expect(el).not_to be_checked
end
# check all contact type options
select_all_el.click
expect(ts_checkboxes).to all(be_checked)
# since all contact type options checked, don't need to select one
within ".top-page-actions" do
click_on "Update CASA Case"
end
has_checked_field? "Youth"
Expand Down
26 changes: 14 additions & 12 deletions spec/system/casa_cases/new_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,17 @@
select "Submitted", from: "casa_case_court_report_status"

find(".ts-control").click
find("span", text: contact_type.name).click
find(".ts-control").click

ts_checkboxes = page.all(".ts-dropdown-content input")
select_all_el = page.find("span[data-test=select-all-input]")
# uncheck all contact type options
select_all_el.click
ts_checkboxes.each do |el|
expect(el).not_to be_checked
end
# check all contact type options
select_all_el.click
expect(ts_checkboxes).to all(be_checked)

select "Test User", from: "casa_case[case_assignments_attributes][0][volunteer_id]"

Expand All @@ -57,7 +66,7 @@
casa_org = build(:casa_org)
admin = create(:casa_admin, casa_org: casa_org)
contact_type_group = create(:contact_type_group, casa_org: casa_org)
contact_type = create(:contact_type, contact_type_group: contact_type_group)
create(:contact_type, contact_type_group: contact_type_group)
case_number = "12345"

sign_in admin
Expand All @@ -69,9 +78,6 @@
select "March", from: "casa_case_birth_month_year_youth_2i"
select five_years, from: "casa_case_birth_month_year_youth_1i"

find(".ts-control").click
find("span", text: contact_type.name).click

within ".actions-cc" do
click_on "Create CASA Case"
end
Expand Down Expand Up @@ -109,7 +115,7 @@
casa_org = build(:casa_org)
admin = create(:casa_admin, casa_org: casa_org)
contact_type_group = create(:contact_type_group, casa_org: casa_org)
contact_type = create(:contact_type, contact_type_group: contact_type_group)
create(:contact_type, contact_type_group: contact_type_group)
case_number = "12345"

sign_in admin
Expand All @@ -121,9 +127,6 @@
select five_years, from: "casa_case_birth_month_year_youth_1i"
check "casa_case_empty_court_date"

find(".ts-control").click
find("span", text: contact_type.name).click

within ".actions-cc" do
click_on "Create CASA Case"
end
Expand Down Expand Up @@ -152,8 +155,7 @@
select "March", from: "casa_case_birth_month_year_youth_2i"
select five_years, from: "casa_case_birth_month_year_youth_1i"

find(".ts-control").click
find("span", text: contact_type.name).click
# 2/14/2025 - by default, all contact types are selected on page load so don't need to manually select

within ".actions-cc" do
click_on "Create CASA Case"
Expand Down
Loading