Skip to content

Commit

Permalink
Merge pull request #304 from alphagov/follow-up-questions
Browse files Browse the repository at this point in the history
Follow up questions
  • Loading branch information
allait authored Feb 22, 2017
2 parents 4016b45 + 74eada6 commit 078c5a4
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 65 deletions.
61 changes: 61 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,67 @@

Records breaking changes from major version bumps

## 21.0.0

PR: [#304](https://github.com/alphagov/digitalmarketplace-frontend-toolkit/pull/304)

### What changed

- The format of the followup questions has changed, so toolkit form macros need to pass in
the `content_question.values_followup` instead of the `content_question.followup`
- Since radio and checkbox questions can have followups too the related macros need to set
`followup=content_question.values_followup`
- Radio, boolean and list-entry questions can be follow-up questions, so related macros
need to set `hidden=question_content.hidden`
- We need to vendor a version of `show-hide-content.js` with added support for multiple
follow-up inputs, so it has to be used instead of the GOV.UK toolkit one

### Example app change

#### Loading the `show-hide-content.js` with support for multiple follow-up questions

Old:
```
//= include ../../../node_modules/govuk_frontend_toolkit/javascripts/govuk/show-hide-content.js
```

New:
```
//= include ../../../bower_components/digitalmarketplace-frontend-toolkit/toolkit/javascripts/show-hide-content.js
```

#### Updating application `form_toolkit` macros

Old:
```jinja
{% macro boolean(question_content, service_data, errors, question_number=None, get_question=None) -%}
{%
with
...
followup=question_content.followup,
...
%}
{% include "toolkit/forms/selection-buttons.html" %}
{% endwith %}
{%- endmacro %}
```

New:
```jinja
{% macro boolean(question_content, service_data, errors, question_number=None, get_question=None) -%}
{%
with
...
followup=question_content.values_followup,
hidden=question_content.hidden,
...
%}
{% include "toolkit/forms/selection-buttons.html" %}
{% endwith %}
{%- endmacro %}
```


## 20.0.0

PR: [#302](https://github.com/alphagov/digitalmarketplace-frontend-toolkit/pull/302)
Expand Down
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20.0.1
21.0.0
72 changes: 67 additions & 5 deletions pages_builder/pages/forms/combinations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ grid: column-two-thirds
bodyEnd: >
<script type="text/javascript" src="../public/javascripts/govuk_frontend_toolkit/vendor/polyfills/bind.js"></script>
<script type="text/javascript" src="../public/javascripts/govuk_frontend_toolkit/govuk/selection-buttons.js"></script>
<script type="text/javascript" src="../public/javascripts/govuk_frontend_toolkit/govuk/show-hide-content.js"></script>
<script type="text/javascript" src="../public/javascripts/show-hide-content.js"></script>
<script type="text/javascript" src="../public/javascripts/vendor/hogan-3.0.2.min.js"></script>
<script type="text/javascript" src="../public/javascripts/list-entry.js"></script>
examples:
- |
<form>
<!-- `followup` field must be set to the `name` of the evidence textbox -->
{%
with
followup = "requirement-evidence-10",
followup = {True: ["requirement-evidence-10"]},
name = "requirement-10",
question = "Can you do this essential requirement",
value = true,
Expand All @@ -35,10 +36,9 @@ examples:
- |
<form>
<!-- `followup` field must be set to the `name` of the evidence textbox -->
{%
with
followup = "requirement-evidence-20",
followup = {True: ["requirement-evidence-20"]},
name = "evidence-20",
question = "Can you do this essential requirement - with errors",
value = true,
Expand All @@ -58,3 +58,65 @@ examples:
{% include "forms/textbox.html" %}
{% endwith %}
</form>
- |
<form>
{%
with
followup = {"Other": ["requirement-evidence-30"]},
name = "evidence-30",
question = "Checkboxes question with follow-up",
value = ["Option 1", "Other"],
type = "checkboxes",
options = [{"value": "Option 1", "label": "Option 1"},
{"value": "Option 2", "label": "Option 2"},
{"value": "Other", "label": "Other"}]
%}
{% include "forms/selection-buttons.html" %}
{% endwith %}
{%
with
large = true,
question = "Follow-up question for 'Other'",
name = "requirement-evidence-30",
hidden = true,
value = ""
%}
{% include "forms/textbox.html" %}
{% endwith %}
</form>
- |
<form>
{%
with
followup = {True: ["requirement-evidence-40", "requirement-boolean-followup-40"]},
name = "evidence-40",
question = "Question with multiple follow-ups",
value = true,
type = "boolean"
%}
{% include "forms/selection-buttons.html" %}
{% endwith %}
{%
with
name = "requirement-boolean-followup-40",
question = "Boolean follow-up",
hidden = true,
type = "boolean"
%}
{% include "forms/selection-buttons.html" %}
{% endwith %}
{%
with
large = true,
id = "requirement-evidence-40",
question = "List follow-up",
name = "requirement-evidence-40",
number_of_items=10,
hidden = true,
value = ""
%}
{% include "forms/list-entry.html" %}
{% endwith %}
</form>
4 changes: 3 additions & 1 deletion pages_builder/pages/forms/selection-buttons.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,9 @@ examples:
question: Can you do this essential requirement
question_number: 93
value: true
followup: requirement-evidence-110
followup:
true:
- requirement-evidence-110
surrounding_html: |
<form>
{{ example | safe }}
Expand Down
194 changes: 194 additions & 0 deletions toolkit/javascripts/show-hide-content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
;(function (global) {
'use strict'

var $ = global.jQuery
var GOVUK = global.GOVUK || {}

function ShowHideContent () {
var self = this

// Radio and Checkbox selectors
var selectors = {
namespace: 'ShowHideContent',
radio: '.block-label[data-target] input[type="radio"]',
checkbox: '.block-label[data-target] input[type="checkbox"]'
}

// Escape name attribute for use in DOM selector
function escapeElementName (str) {
var result = str.replace('[', '\\[').replace(']', '\\]')
return result
}

// Adds ARIA attributes to control + associated content
function initToggledContent () {
var $control = $(this)
var $content = getToggledContent($control)
var contentIds = ''

// Set aria-controls and defaults
if ($content.length) {
$content.each(function () {
$(this).attr('aria-hidden', 'true')
contentIds += ' ' + $(this).attr('id')
})

$control.attr('aria-controls', contentIds.trim())
$control.attr('aria-expanded', 'false')
}
}

// Return toggled content for control
function getToggledContent ($control) {
var targetIds = $control.attr('aria-controls')

// ARIA attributes aren't set before init
if (!targetIds) {
targetIds = $control.closest('label').data('target')
}

// turn a space-separated list of ids into a comma-separated css id selector
// ie, 'id-1 id-2 id-3' becomes '#id-1, #id-2, #id-3'
if (targetIds) {
targetIds = targetIds
.split(' ')
.map(function (targetId) { return '#' + targetId })
.join(', ')
}

// Find show/hide content by id
return $(targetIds)
}

// Show toggled content for control
function showToggledContent ($control, $content) {
if (!$content.length) {
return
}

// Show content
if ($content.hasClass('js-hidden')) {
$content.removeClass('js-hidden')
$content.attr('aria-hidden', 'false')

// If the controlling input, update aria-expanded
if ($control.attr('aria-controls')) {
$control.attr('aria-expanded', 'true')
}
}
}

// Hide toggled content for control
function hideToggledContent ($control, $content) {
$content = $content || getToggledContent($control)

if (!$content.length) {
return
}

// Hide content
if (!$content.hasClass('js-hidden')) {
$content.addClass('js-hidden')
$content.attr('aria-hidden', 'true')

// If the controlling input, update aria-expanded
if ($control.attr('aria-controls')) {
$control.attr('aria-expanded', 'false')
}
}
}

// Handle radio show/hide
function handleRadioContent ($control, $content) {
// All radios in this group which control content
var selector = selectors.radio + '[name=' + escapeElementName($control.attr('name')) + '][aria-controls]'
var $form = $control.closest('form')
var $radios = $form.length ? $form.find(selector) : $(selector)

// Hide content for radios in group
$radios.each(function () {
hideToggledContent($(this))
})

// Select content for this control
if ($control.is('[aria-controls]')) {
showToggledContent($control, $content)
}
}

// Handle checkbox show/hide
function handleCheckboxContent ($control, $content) {
// Show checkbox content
if ($control.is(':checked')) {
showToggledContent($control, $content)
} else { // Hide checkbox content
hideToggledContent($control, $content)
}
}

// Set up event handlers etc
function init ($container, elementSelector, eventSelectors, handler) {
$container = $container || $(document.body)

// Handle control clicks
function deferred () {
var $control = $(this)
handler($control, getToggledContent($control))
}

// Prepare ARIA attributes
var $controls = $(elementSelector)
$controls.each(initToggledContent)

// Handle events
$.each(eventSelectors, function (idx, eventSelector) {
$container.on('click.' + selectors.namespace, eventSelector, deferred)
})

// Any already :checked on init?
if ($controls.is(':checked')) {
$controls.filter(':checked').each(deferred)
}
}

// Get event selectors for all radio groups
function getEventSelectorsForRadioGroups () {
var radioGroups = []

// Build an array of radio group selectors
return $(selectors.radio).map(function () {
var groupName = $(this).attr('name')

if ($.inArray(groupName, radioGroups) === -1) {
radioGroups.push(groupName)
return 'input[type="radio"][name="' + $(this).attr('name') + '"]'
}
return null
})
}

// Set up radio show/hide content for container
self.showHideRadioToggledContent = function ($container) {
init($container, selectors.radio, getEventSelectorsForRadioGroups(), handleRadioContent)
}

// Set up checkbox show/hide content for container
self.showHideCheckboxToggledContent = function ($container) {
init($container, selectors.checkbox, [selectors.checkbox], handleCheckboxContent)
}

// Remove event handlers
self.destroy = function ($container) {
$container = $container || $(document.body)
$container.off('.' + selectors.namespace)
}
}

ShowHideContent.prototype.init = function ($container) {
this.showHideRadioToggledContent($container)
this.showHideCheckboxToggledContent($container)
}

GOVUK.ShowHideContent = ShowHideContent
global.GOVUK = GOVUK
})(window)
2 changes: 1 addition & 1 deletion toolkit/templates/forms/list-entry.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
{% elif message %}
<div class="message-wrapper">
{% endif %}
<fieldset class="question {% if first_question %}first-question{% endif %}" id="{{ id }}">
<fieldset class="question{% if first_question %} first-question{% endif %}{% if hidden %} js-hidden related-information{% endif %}" id="{{ id }}">
<legend>
<span class="question-heading">
{% if question_number %}
Expand Down
Loading

0 comments on commit 078c5a4

Please sign in to comment.