Browse Source

CLDC-341: Conditional showing and hiding of question on a page (#29)

* Use Stimulus controller for showing and hiding conditional questions for radio questions
pull/33/head
Daniel Baark 3 years ago committed by GitHub
parent
commit
a831a70aab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      Gemfile
  2. 7
      Gemfile.lock
  3. 15
      app/helpers/conditional_questions_helper.rb
  4. 27
      app/javascript/controllers/conditional_question_controller.js
  5. 7
      app/javascript/controllers/hello_controller.js
  6. 12
      app/javascript/controllers/index.js
  7. 6
      app/views/form/_radio_question.html.erb
  8. 2
      app/views/form/page.html.erb
  9. 4
      config/forms/2021_2022.json
  10. 4
      package.json
  11. 22
      spec/features/case_log_spec.rb
  12. 12
      spec/helpers/check_answers_helper_spec.rb
  13. 28
      spec/helpers/conditional_questions_helper_spec.rb
  14. 8
      spec/rails_helper.rb
  15. 2777
      yarn.lock

2
Gemfile

@ -29,7 +29,7 @@ group :development, :test do
gem "dotenv-rails" gem "dotenv-rails"
gem "factory_bot_rails" gem "factory_bot_rails"
gem "pry-byebug" gem "pry-byebug"
# gem "selenium-webdriver", require: false gem "selenium-webdriver", require: false
gem "simplecov", require: false gem "simplecov", require: false
%w[rspec-core rspec-expectations rspec-mocks rspec-rails rspec-support].each do |lib| %w[rspec-core rspec-expectations rspec-mocks rspec-rails rspec-support].each do |lib|
gem lib, git: "https://github.com/rspec/#{lib}.git", branch: "main", require: false gem lib, git: "https://github.com/rspec/#{lib}.git", branch: "main", require: false

7
Gemfile.lock

@ -147,7 +147,7 @@ GEM
activemodel (>= 6.0) activemodel (>= 6.0)
railties (>= 6.0) railties (>= 6.0)
view_component (~> 2.39.0) view_component (~> 2.39.0)
govuk_design_system_formbuilder (2.7.4) govuk_design_system_formbuilder (2.7.5)
actionview (>= 6.0) actionview (>= 6.0)
activemodel (>= 6.0) activemodel (>= 6.0)
activesupport (>= 6.0) activesupport (>= 6.0)
@ -267,6 +267,7 @@ GEM
rubocop (~> 1.0) rubocop (~> 1.0)
rubocop-ast (>= 1.1.0) rubocop-ast (>= 1.1.0)
ruby-progressbar (1.11.0) ruby-progressbar (1.11.0)
rubyzip (2.3.2)
sass (3.7.4) sass (3.7.4)
sass-listen (~> 4.0.0) sass-listen (~> 4.0.0)
sass-listen (4.0.0) sass-listen (4.0.0)
@ -276,6 +277,9 @@ GEM
sass (~> 3.5, >= 3.5.5) sass (~> 3.5, >= 3.5.5)
scss_lint-govuk (0.2.0) scss_lint-govuk (0.2.0)
scss_lint scss_lint
selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
semantic_range (3.0.0) semantic_range (3.0.0)
simplecov (0.21.2) simplecov (0.21.2)
docile (~> 1.1) docile (~> 1.1)
@ -349,6 +353,7 @@ DEPENDENCIES
rubocop-performance rubocop-performance
rubocop-rails rubocop-rails
scss_lint-govuk scss_lint-govuk
selenium-webdriver
simplecov simplecov
tzinfo-data tzinfo-data
web-console (>= 4.1.0) web-console (>= 4.1.0)

15
app/helpers/conditional_questions_helper.rb

@ -0,0 +1,15 @@
module ConditionalQuestionsHelper
def conditional_questions_for_page(page)
page["questions"].values.map { |question|
question["conditional_for"]
}.compact.map(&:keys).flatten
end
def display_question_key_div(page_info, question_key)
if conditional_questions_for_page(page_info).include?(question_key)
"<div id=#{question_key}_div style='display:none;'>".html_safe
else
"<div id=#{question_key}_div>".html_safe
end
end
end

27
app/javascript/controllers/conditional_question_controller.js

@ -0,0 +1,27 @@
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
initialize() {
this.displayConditional()
}
displayConditional() {
if(this.element.checked) {
let selected = this.element.value
let conditional_for = JSON.parse(this.element.dataset.info)
Object.entries(conditional_for).forEach(([key, values]) => {
let div = document.getElementById(key + "_div")
if(values.includes(selected)) {
div.style.display = "block"
} else {
div.style.display = "none"
let buttons = document.getElementsByName(key)
Object.entries(buttons).forEach(([idx, button]) => {
button.checked = false;
})
}
})
}
}
}

7
app/javascript/controllers/hello_controller.js

@ -1,7 +0,0 @@
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
this.element.textContent = "Hello World!"
}
}

12
app/javascript/controllers/index.js

@ -1,7 +1,9 @@
// This file is auto-generated by ./bin/rails stimulus:manifest:update // Load all the controllers within this directory and all subdirectories.
// Run that command whenever you add a new controller // Controller files must be named *_controller.js.
import { application } from "./application" import { Application } from "@hotwired/stimulus"
import { definitionsFromContext } from "@hotwired/stimulus-webpack-helpers"
import HelloController from "./hello_controller" const application = Application.start()
application.register("hello", HelloController) const context = require.context("controllers", true, /_controller\.js$/)
application.load(definitionsFromContext(context))

6
app/views/form/_radio_question.html.erb

@ -6,6 +6,12 @@
<% question["answer_options"].map do |key, val| %> <% question["answer_options"].map do |key, val| %>
<% if key.starts_with?("divider") %> <% if key.starts_with?("divider") %>
<%= f.govuk_radio_divider %> <%= f.govuk_radio_divider %>
<% elsif question["conditional_for"] %>
<%= f.govuk_radio_button question_key, val, label: { text: val },
"data-controller": "conditional-question",
"data-action": "conditional-question#displayConditional",
"data-info": question["conditional_for"].to_json
%>
<% else %> <% else %>
<%= f.govuk_radio_button question_key, val, label: { text: val } %> <%= f.govuk_radio_button question_key, val, label: { text: val } %>
<% end %> <% end %>

2
app/views/form/page.html.erb

@ -14,7 +14,9 @@
<%= form_with action: '/case_logs', method: "next_page", builder: GOVUKDesignSystemFormBuilder::FormBuilder do |f| %> <%= form_with action: '/case_logs', method: "next_page", builder: GOVUKDesignSystemFormBuilder::FormBuilder do |f| %>
<% page_info["questions"].map do |question_key, question| %> <% page_info["questions"].map do |question_key, question| %>
<%= display_question_key_div(page_info, question_key)%>
<%= render partial: "form/#{question["type"]}_question", locals: { question_key: question_key, question: question, f: f } %> <%= render partial: "form/#{question["type"]}_question", locals: { question_key: question_key, question: question, f: f } %>
</div>
<% end %> <% end %>
<%= f.hidden_field :previous_page, value: page_key %> <%= f.hidden_field :previous_page, value: page_key %>

4
config/forms/2021_2022.json

@ -292,6 +292,10 @@
"1": "Yes - a reserve", "1": "Yes - a reserve",
"2": "No", "2": "No",
"3": "Prefer not to say" "3": "Prefer not to say"
},
"conditional_for": {
"armed_forces_active": ["Yes - a regular", "Yes - a reserve"],
"armed_forces_injured": ["Yes - a regular", "Yes - a reserve"]
} }
}, },
"armed_forces_active": { "armed_forces_active": {

4
package.json

@ -3,6 +3,7 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@hotwired/stimulus": "^3.0.0", "@hotwired/stimulus": "^3.0.0",
"@hotwired/stimulus-webpack-helpers": "^1.0.1",
"@hotwired/turbo": "^7.0.0-rc.3", "@hotwired/turbo": "^7.0.0-rc.3",
"@hotwired/turbo-rails": "^7.0.0-rc.3", "@hotwired/turbo-rails": "^7.0.0-rc.3",
"@rails/actioncable": "^6.0.0", "@rails/actioncable": "^6.0.0",
@ -10,8 +11,7 @@
"@rails/ujs": "^6.0.0", "@rails/ujs": "^6.0.0",
"@rails/webpacker": "5.4.0", "@rails/webpacker": "5.4.0",
"govuk-frontend": "^3.13.0", "govuk-frontend": "^3.13.0",
"stimulus": "^2.0.0", "stimulus": "^3.0.0",
"stimulus-use": "^0.26.0",
"webpack": "^4.46.0", "webpack": "^4.46.0",
"webpack-cli": "^3.3.12" "webpack-cli": "^3.3.12"
}, },

22
spec/features/case_log_spec.rb

@ -218,4 +218,26 @@ RSpec.describe "Test Features" do
end end
end end
end end
describe "Conditional questions" do
context "given a page where some questions are only conditionally shown, depending on how you answer the first question" do
it "initially hides conditional questions" do
visit("/case_logs/#{id}/armed_forces")
expect(page).not_to have_selector("#armed_forces_injured_div")
end
it "shows conditional questions if the required answer is selected and hides it again when a different answer option is selected", js: true do
visit("/case_logs/#{id}/armed_forces")
# Something about our styling makes the selenium webdriver think the actual radio buttons are not visible so we allow label click here
choose("armed-forces-yes-a-regular-field", allow_label_click: true)
expect(page).to have_selector("#armed_forces_injured_div")
choose("armed-forces-injured-no-field", allow_label_click: true)
expect(find_field("armed-forces-injured-no-field", visible: false).checked?).to be_truthy
choose("armed-forces-no-field", allow_label_click: true)
expect(page).not_to have_selector("#armed_forces_injured_div")
choose("armed-forces-yes-a-regular-field", allow_label_click: true)
expect(find_field("armed-forces-injured-no-field", visible: false).checked?).to be_falsey
end
end
end
end end

12
spec/helpers/check_answers_helper_spec.rb

@ -1,11 +1,11 @@
require "rails_helper" require "rails_helper"
RSpec.describe CheckAnswersHelper do RSpec.describe CheckAnswersHelper do
describe "Get answered questions total" do let(:case_log) { FactoryBot.create(:case_log) }
let!(:case_log) { FactoryBot.create(:case_log) } let(:form) { Form.new(2021, 2022) }
@form = Form.new(2021, 2022) let(:subsection_pages) { form.pages_for_subsection("income_and_benefits") }
subsection_pages = @form.pages_for_subsection("income_and_benefits")
describe "Get answered questions total" do
it "returns 0 if no questions are answered" do it "returns 0 if no questions are answered" do
expect(get_answered_questions_total(subsection_pages, case_log)).to equal(0) expect(get_answered_questions_total(subsection_pages, case_log)).to equal(0)
end end
@ -17,10 +17,6 @@ RSpec.describe CheckAnswersHelper do
end end
describe "Get total number of questions" do describe "Get total number of questions" do
let!(:case_log) { FactoryBot.create(:case_log) }
@form = Form.new(2021, 2022)
subsection_pages = @form.pages_for_subsection("income_and_benefits")
it "returns the total number of questions for a subsection" do it "returns the total number of questions for a subsection" do
expect(get_total_number_of_questions(subsection_pages)).to eq(4) expect(get_total_number_of_questions(subsection_pages)).to eq(4)
end end

28
spec/helpers/conditional_questions_helper_spec.rb

@ -0,0 +1,28 @@
require "rails_helper"
RSpec.describe ConditionalQuestionsHelper do
let(:form) { Form.new(2021, 2022) }
let(:page_key) { "armed_forces" }
let(:page) { form.all_pages[page_key] }
describe "conditional questions for page" do
let(:conditional_pages) { %w[armed_forces_active armed_forces_injured] }
it "returns the question keys of all conditional questions on the given page" do
expect(conditional_questions_for_page(page)).to eq(conditional_pages)
end
end
describe "display question key div" do
let(:question_key) { "armed_forces" }
let(:conditional_question_key) { "armed_forces_injured" }
it "returns a non visible div for conditional questions" do
expect(display_question_key_div(page, conditional_question_key)).to match("style='display:none;'")
end
it "returns a visible div for conditional questions" do
expect(display_question_key_div(page, question_key)).not_to match("style='display:none;'")
end
end
end

8
spec/rails_helper.rb

@ -6,6 +6,10 @@ require File.expand_path("../config/environment", __dir__)
abort("The Rails environment is running in production mode!") if Rails.env.production? abort("The Rails environment is running in production mode!") if Rails.env.production?
require "rspec/rails" require "rspec/rails"
require "capybara/rspec" require "capybara/rspec"
# Comment to run `js: true specs` with visible browser interaction
Capybara.javascript_driver = :selenium_headless
# Add additional requires below this line. Rails is not loaded until this point! # Add additional requires below this line. Rails is not loaded until this point!
# Requires supporting ruby files with custom matchers and macros, etc, in # Requires supporting ruby files with custom matchers and macros, etc, in
@ -62,4 +66,8 @@ RSpec.configure do |config|
config.filter_rails_from_backtrace! config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via: # arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name") # config.filter_gems_from_backtrace("gem name")
# Silence capybara logging puma start up messages to stdout on first js: true
# spec
Capybara.server = :puma, { Silent: true }
end end

2777
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save