From b303c930322fe6916f08063e74b4c60022389706 Mon Sep 17 00:00:00 2001 From: Ryan Wold Date: Fri, 2 May 2025 10:57:48 -0700 Subject: [PATCH 1/7] update gems --- Gemfile.lock | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index ebfeed2b9..7987d9873 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -107,28 +107,28 @@ GEM aws-sdk-ses (~> 1, >= 1.50.0) aws-sdk-sesv2 (~> 1, >= 1.34.0) aws-eventstream (1.3.2) - aws-partitions (1.1090.0) - aws-sdk-core (3.222.2) + aws-partitions (1.1096.0) + aws-sdk-core (3.223.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) base64 jmespath (~> 1, >= 1.6.1) logger - aws-sdk-kms (1.99.0) + aws-sdk-kms (1.100.0) aws-sdk-core (~> 3, >= 3.216.0) aws-sigv4 (~> 1.5) aws-sdk-rails (5.1.0) aws-sdk-core (~> 3) railties (>= 7.1.0) - aws-sdk-s3 (1.183.0) + aws-sdk-s3 (1.185.0) aws-sdk-core (~> 3, >= 3.216.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sdk-ses (1.82.0) + aws-sdk-ses (1.83.0) aws-sdk-core (~> 3, >= 3.216.0) aws-sigv4 (~> 1.5) - aws-sdk-sesv2 (1.74.0) + aws-sdk-sesv2 (1.75.0) aws-sdk-core (~> 3, >= 3.216.0) aws-sigv4 (~> 1.5) aws-sigv4 (1.11.0) @@ -186,7 +186,7 @@ GEM coercible (1.0.0) descendants_tracker (~> 0.0.1) concurrent-ruby (1.3.5) - connection_pool (2.5.1) + connection_pool (2.5.3) crass (1.0.6) csv (3.3.4) database_cleaner (2.1.0) @@ -219,7 +219,7 @@ GEM railties (>= 5.0.0) faker (3.5.1) i18n (>= 1.8.11, < 2) - faraday (2.13.0) + faraday (2.13.1) faraday-net_http (>= 2.0, < 3.5) json logger @@ -280,7 +280,7 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.10.2) + json (2.11.3) json-jwt (1.16.7) activesupport (>= 4.2) aes_key_wrap @@ -327,7 +327,7 @@ GEM mime-types (3.6.2) logger mime-types-data (~> 3.2015) - mime-types-data (3.2025.0415) + mime-types-data (3.2025.0429) mini_magick (5.2.0) benchmark logger @@ -336,11 +336,11 @@ GEM minitest (5.25.5) msgpack (1.8.0) multi_json (1.15.0) - multi_xml (0.7.1) + multi_xml (0.7.2) bigdecimal (~> 3.1) net-http (0.6.0) uri - net-imap (0.5.7) + net-imap (0.5.8) date net-protocol net-pop (0.1.2) @@ -349,7 +349,7 @@ GEM timeout net-smtp (0.5.1) net-protocol - newrelic_rpm (9.18.0) + newrelic_rpm (9.19.0) nio4r (2.7.4) nokogiri (1.18.8) mini_portile2 (~> 2.8.2) @@ -407,10 +407,10 @@ GEM pry (0.15.2) coderay (~> 1.1) method_source (~> 1.0) - psych (5.2.3) + psych (5.2.4) date stringio - public_suffix (6.0.1) + public_suffix (6.0.2) puma (6.6.0) nio4r (~> 2.0) racc (1.8.1) @@ -493,24 +493,24 @@ GEM rolify (6.0.1) rspec-core (3.13.3) rspec-support (~> 3.13.0) - rspec-expectations (3.13.3) + rspec-expectations (3.13.4) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-mocks (3.13.2) + rspec-mocks (3.13.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-rails (7.1.1) - actionpack (>= 7.0) - activesupport (>= 7.0) - railties (>= 7.0) + rspec-rails (8.0.0) + actionpack (>= 7.2) + activesupport (>= 7.2) + railties (>= 7.2) rspec-core (~> 3.13) rspec-expectations (~> 3.13) rspec-mocks (~> 3.13) rspec-support (~> 3.13) - rspec-support (3.13.2) + rspec-support (3.13.3) rspec_junit_formatter (0.6.0) rspec-core (>= 2, < 4, != 2.12.0) - rubocop (1.75.3) + rubocop (1.75.4) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -555,7 +555,7 @@ GEM rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) - sidekiq (8.0.2) + sidekiq (8.0.3) connection_pool (>= 2.5.0) json (>= 2.9.0) logger (>= 1.6.2) From 3973629f728df477a8b64d2a97198b442c2a03d8 Mon Sep 17 00:00:00 2001 From: Ryan Wold Date: Fri, 2 May 2025 12:43:19 -0700 Subject: [PATCH 2/7] a11-v2 with 5 radio buttons first 3 show "what could have gone better" last 2 show "what went well" --- app/models/form.rb | 5 +- .../_form_a11_v2_radio_script.html.erb | 52 +++++++++++++++++++ .../question_types/_radio_buttons.html.erb | 7 +-- 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 app/views/components/_form_a11_v2_radio_script.html.erb diff --git a/app/models/form.rb b/app/models/form.rb index c624d1d57..815c70bd6 100644 --- a/app/models/form.rb +++ b/app/models/form.rb @@ -57,7 +57,8 @@ def self.filtered_forms(user, aasm_state) def self.kinds [ "a11", - "a11_v2", # launched fall 2023 + "a11_v2", # launched Fall 2023 + "a11_v2_radio", # launched May 2025 "a11_yes_no", "open_ended", "other", # TODO: deprecate in favor of custom, @@ -289,7 +290,7 @@ def self.send_inactive_form_emails_since(days_ago) def self.find_inactive_forms_since(days_ago) min_time = Time.now - days_ago.days max_time = Time.now - (days_ago - 1).days - Form.published.where("last_response_created_at BETWEEN ? AND ?", min_time, max_time) + Form.non_templates.published.where("last_response_created_at BETWEEN ? AND ?", min_time, max_time) end def deployable_form? diff --git a/app/views/components/_form_a11_v2_radio_script.html.erb b/app/views/components/_form_a11_v2_radio_script.html.erb new file mode 100644 index 000000000..70efc876d --- /dev/null +++ b/app/views/components/_form_a11_v2_radio_script.html.erb @@ -0,0 +1,52 @@ +// Assumes: 4 questions: +// 1. 5 radio buttons with values 1-5 +// 2. positive checkbox indicators +// 3. negative checkbox indicators +// 4. open text +// Hides the 2nd and 3rd questions to start +// reveals 2 when selecting thumbs up +// reveals 3 when selecting thumbs down +<% + question_1 = form.ordered_questions.find { |q| q.answer_field == "answer_01"} + question_2 = form.ordered_questions.find { |q| q.answer_field == "answer_02"} + question_3 = form.ordered_questions.find { |q| q.answer_field == "answer_03"} +%> + +document.addEventListener('onTouchpointsFormLoaded', function(e) { + const formElement = e.detail.formComponent.formElement(); + const q2_container = formElement.querySelector("#<%= dom_id(question_2) %>"); + const q3_container = formElement.querySelector("#<%= dom_id(question_3) %>"); + + function hideQ2() { + q2_container.style.display = 'none'; + } + function showQ2() { + q2_container.style.display = 'block'; + } + + function hideQ3() { + q3_container.style.display = 'none'; + } + function showQ3() { + q3_container.style.display = 'block'; + } + + function showAndHideQuestions(selectedOption) { + if (selectedOption === "1" || selectedOption === "2" || selectedOption === "3") { + hideQ2() + showQ3() + } else if (selectedOption === "4" || selectedOption === "5") { + showQ2() + hideQ3() + } + } + + formElement.querySelectorAll('input[name="<%= question_1.ui_selector %>"]').forEach((radio) => { + radio.addEventListener('change', (event) => { + showAndHideQuestions(event.target.value); + }); + }); + + hideQ2() + hideQ3() +}) \ No newline at end of file diff --git a/app/views/components/forms/question_types/_radio_buttons.html.erb b/app/views/components/forms/question_types/_radio_buttons.html.erb index 463a74f1c..a0fe6b05d 100644 --- a/app/views/components/forms/question_types/_radio_buttons.html.erb +++ b/app/views/components/forms/question_types/_radio_buttons.html.erb @@ -3,14 +3,15 @@
<% question.question_options.each_with_index do |option, index| %> <% @option_id = dom_id(option) %> -
aria-describedby="<%= "question-id-#{question.id}-help-text" %>" <% end %> > - <%= radio_button_tag(@option_id, option.value, nil, { id: @option_id, name: question.ui_selector, class: "usa-radio__input usa-radio__input--tile", required: question.is_required }) %> - <%= label_tag(@option_id, nil, class: "usa-radio__label") do %><%= option.text %><% end %> + <%= radio_button_tag(question.ui_selector, option.value.to_s, nil, { id: @option_id, class: "usa-radio__input usa-radio__input--tile", required: question.is_required }) %> + <%= label_tag(@option_id, option.text, class: "usa-radio__label") %> <%- if option.other_option %>
<%= label_tag(nil, for: "#{question.ui_selector}_other", class: "usa-input__label") do %><%= t 'form.enter_other_text' %><% end %> From 1c701f4243f9d79b1545d3dc8bfbb5f79f8feb2e Mon Sep 17 00:00:00 2001 From: Ryan Wold Date: Fri, 2 May 2025 12:53:23 -0700 Subject: [PATCH 3/7] render custom scripts for a11_v2_radio forms --- app/views/components/widget/_fba.js.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/components/widget/_fba.js.erb b/app/views/components/widget/_fba.js.erb index 051538e03..d1a65fd38 100644 --- a/app/views/components/widget/_fba.js.erb +++ b/app/views/components/widget/_fba.js.erb @@ -794,7 +794,7 @@ var touchpointFormOptions<%= form.short_uuid %> = { 'css' : "<%= escape_javascript(render partial: 'components/widget/widget', formats: :css, locals: { form: form }) %>", 'loadCSS' : <%= form.load_css %>, 'formSpecificScript' : function() { - <%- if ["a11_v2", "a11_yes_no"].include?(form.kind) %> + <%- if ["a11_v2", "a11_v2_radio", "a11_yes_no"].include?(form.kind) %> <%= render "components/form_#{form.kind}_script", form: form %> <% end %> }, From 6a6b06dced393ed4ed8586f56b76540bf3754570 Mon Sep 17 00:00:00 2001 From: Ryan Wold Date: Fri, 2 May 2025 13:05:06 -0700 Subject: [PATCH 4/7] a11_v2_radio form ensures question 1 is radio buttons --- app/controllers/admin/forms_controller.rb | 1 + app/models/form.rb | 35 +++++++++++++++++++++++ spec/models/form_spec.rb | 2 +- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/app/controllers/admin/forms_controller.rb b/app/controllers/admin/forms_controller.rb index 06b6fc151..36c6d4193 100644 --- a/app/controllers/admin/forms_controller.rb +++ b/app/controllers/admin/forms_controller.rb @@ -218,6 +218,7 @@ def permissions def questions @form.warn_about_not_too_many_questions @form.ensure_a11_v2_format if @form.kind == "a11_v2" + @form.ensure_a11_v2_radio_format if @form.kind == "a11_v2_radio" ensure_form_manager(form: @form) unless @form.template? @questions = @form.ordered_questions end diff --git a/app/models/form.rb b/app/models/form.rb index 815c70bd6..c9bbe5d03 100644 --- a/app/models/form.rb +++ b/app/models/form.rb @@ -735,6 +735,41 @@ def ensure_a11_v2_format end end + def ensure_a11_v2_radio_format + question_1 = self.ordered_questions.find { |q| q.answer_field == "answer_01" } + if question_1.question_type != 'radio_buttons' + errors.add(:base, "The question for `answer_01` must be a Radio Buttons component with 5 options, with values 1-5") + end + + # ensure the form has the 4 required questions + required_elements = ["answer_01", "answer_02", "answer_03", "answer_04"] + unless contains_elements?(questions.collect(&:answer_field), required_elements) + errors.add(:base, "The A-11 v2 form must have questions for #{required_elements.to_sentence}") + end + + # ensure the positive indicators include ease and effectiveness + question_2 = self.ordered_questions.find { |q| q.answer_field == "answer_02" } + question_options = question_2.question_options + + question_option_values = question_options.collect(&:value) + required_options = ["effectiveness", "ease"] + missing_options = required_options - question_option_values + if missing_options.any? + errors.add(:base, "The question options for Question 2 must include: #{missing_options.join(', ')}") + end + + # ensure the positive indicators include ease and effectiveness + question_3 = self.ordered_questions.find { |q| q.answer_field == "answer_03" } + question_options = question_3.question_options + + question_option_values = question_options.collect(&:value) + required_options = ["effectiveness", "ease"] + missing_options = required_options - question_option_values + if missing_options.any? + errors.add(:base, "The question options for Question 3 must include: #{missing_options.join(', ')}") + end + end + def warn_about_not_too_many_questions if questions.size > 12 errors.add(:base, "Touchpoints supports a maximum of 20 questions. There are currently #{questions_count} questions. Fewer questions tend to yield higher response rates.") diff --git a/spec/models/form_spec.rb b/spec/models/form_spec.rb index a9ce907c3..9a0c42d5c 100644 --- a/spec/models/form_spec.rb +++ b/spec/models/form_spec.rb @@ -87,7 +87,7 @@ end it 'adds an error for kind' do - expect(form_with_invalid_kind.errors.messages[:kind]).to eq(['kind must be one of the following: a11, a11_v2, a11_yes_no, custom, open_ended, other, recruiter, yes_no']) + expect(form_with_invalid_kind.errors.messages[:kind]).to eq(['kind must be one of the following: a11, a11_v2, a11_v2_radio, a11_yes_no, custom, open_ended, other, recruiter, yes_no']) end end From 931d9fe4baecd4efd1ad9ac8afc5e373db81c788 Mon Sep 17 00:00:00 2001 From: Ryan Wold Date: Fri, 2 May 2025 14:58:28 -0700 Subject: [PATCH 5/7] spec conditional toggles --- .../question_types/_radio_buttons.html.erb | 9 ++- spec/factories/form.rb | 71 +++++++++++++++++++ spec/features/touchpoints_spec.rb | 48 ++++++++++++- 3 files changed, 124 insertions(+), 4 deletions(-) diff --git a/app/views/components/forms/question_types/_radio_buttons.html.erb b/app/views/components/forms/question_types/_radio_buttons.html.erb index a0fe6b05d..bf61d2a12 100644 --- a/app/views/components/forms/question_types/_radio_buttons.html.erb +++ b/app/views/components/forms/question_types/_radio_buttons.html.erb @@ -10,10 +10,15 @@ aria-describedby="<%= "question-id-#{question.id}-help-text" %>" <% end %> > - <%= radio_button_tag(question.ui_selector, option.value.to_s, nil, { id: @option_id, class: "usa-radio__input usa-radio__input--tile", required: question.is_required }) %> + <%= radio_button_tag(question.ui_selector, option[:value], nil, { + id: @option_id, + class: "usa-radio__input usa-radio__input--tile", + required: question.is_required + }) %> <%= label_tag(@option_id, option.text, class: "usa-radio__label") %> <%- if option.other_option %> -
+
<%= label_tag(nil, for: "#{question.ui_selector}_other", class: "usa-input__label") do %><%= t 'form.enter_other_text' %><% end %> Date: Fri, 2 May 2025 15:08:05 -0700 Subject: [PATCH 6/7] no response for answer_03 --- spec/features/touchpoints_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/touchpoints_spec.rb b/spec/features/touchpoints_spec.rb index 293407510..31bb0b1af 100644 --- a/spec/features/touchpoints_spec.rb +++ b/spec/features/touchpoints_spec.rb @@ -544,7 +544,7 @@ latest_submission = Submission.ordered.first expect(latest_submission.answer_01).to eq '1' expect(latest_submission.answer_02).to eq 'effectiveness,transparency' - expect(latest_submission.answer_03).to eq "" + expect(latest_submission.answer_03).to eq nil expect(latest_submission.answer_04).to eq "" end end From d89212ce555d4ed15b0efd3650237ddadef02217 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 15:17:43 +0000 Subject: [PATCH 7/7] Bump rack-session from 2.1.0 to 2.1.1 Bumps [rack-session](https://github.com/rack/rack-session) from 2.1.0 to 2.1.1. - [Release notes](https://github.com/rack/rack-session/releases) - [Changelog](https://github.com/rack/rack-session/blob/v2.1.1/releases.md) - [Commits](https://github.com/rack/rack-session/compare/v2.1.0...v2.1.1) --- updated-dependencies: - dependency-name: rack-session dependency-version: 2.1.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7987d9873..be5895b16 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -414,7 +414,7 @@ GEM puma (6.6.0) nio4r (~> 2.0) racc (1.8.1) - rack (3.1.13) + rack (3.1.14) rack-attack (6.7.0) rack (>= 1.0, < 4) rack-cors (2.0.2) @@ -423,7 +423,7 @@ GEM base64 (>= 0.1.0) logger (>= 1.6.0) rack (>= 3.0.0, < 4) - rack-session (2.1.0) + rack-session (2.1.1) base64 (>= 0.1.0) rack (>= 3.0.0) rack-test (2.2.0)