Allow dependencies between questions (#1202)

- [x] data model
- [x] api
- [x] backend editor
- [x] backend validation logic
- [x] frontend display logic
- [x] frontend validation logic
- [x] test checkout step
- [x] test modify order in frontend
- [x] test modify order in backend
- [x] validation tests
- [x] correctly evaluate dependency tree in frontend?
- [x] copy events
This commit is contained in:
Raphael Michel
2019-03-13 16:49:20 +01:00
committed by GitHub
parent d10cbd07a7
commit f95e8f374d
22 changed files with 825 additions and 211 deletions

View File

@@ -6,6 +6,7 @@ function gettext(msgid) {
}
return msgid;
}
function ngettext(singular, plural, count) {
if (typeof django !== 'undefined' && typeof django.ngettext !== 'undefined') {
return django.ngettext(singular, plural, count);
@@ -14,7 +15,7 @@ function ngettext(singular, plural, count) {
}
var form_handlers = function (el) {
el.find(".datetimepicker").each(function() {
el.find(".datetimepicker").each(function () {
$(this).datetimepicker({
format: $("body").attr("data-datetimeformat"),
locale: $("body").attr("data-datetimelocale"),
@@ -37,7 +38,7 @@ var form_handlers = function (el) {
}
});
el.find(".datepickerfield").each(function() {
el.find(".datepickerfield").each(function () {
var opts = {
format: $("body").attr("data-dateformat"),
locale: $("body").attr("data-datetimelocale"),
@@ -71,7 +72,7 @@ var form_handlers = function (el) {
}
});
el.find(".timepickerfield").each(function() {
el.find(".timepickerfield").each(function () {
var opts = {
format: $("body").attr("data-timeformat"),
locale: $("body").attr("data-datetimelocale"),
@@ -90,7 +91,7 @@ var form_handlers = function (el) {
}
};
$(this).datetimepicker(opts);
});
});
el.find("script[data-replace-with-qr]").each(function () {
var $div = $("<div>");
@@ -104,7 +105,10 @@ var form_handlers = function (el) {
}
);
});
}
el.find("input[name*=question], select[name*=question]").change(questions_toggle_dependent);
questions_toggle_dependent();
};
$(function () {
@@ -130,7 +134,7 @@ $(function () {
$("#voucher-box").slideDown();
$("#voucher-toggle").slideUp();
});
$('[data-toggle="tooltip"]').tooltip();
$("#ajaxerr").on("click", ".ajaxerr-close", ajaxErrDialog.hide);
@@ -140,7 +144,7 @@ $(function () {
$('.toggle-variation-description').click(function () {
$(this).parent().find('.addon-variation-description').slideToggle();
});
// Copy answers
$(".js-copy-answers").click(function (e) {
e.preventDefault();
@@ -153,7 +157,7 @@ $(function () {
// Subevent choice
if ($(".subevent-toggle").length) {
$(".subevent-list").hide();
$(".subevent-toggle").css("display", "block").click(function() {
$(".subevent-toggle").css("display", "block").click(function () {
$(".subevent-list").slideToggle(300);
});
}
@@ -165,7 +169,7 @@ $(function () {
var update_cart_form = function () {
var is_enabled = $(".product-row input[type=checkbox]:checked, .variations input[type=checkbox]:checked, .product-row input[type=radio]:checked, .variations input[type=radio]:checked").length;
if (!is_enabled) {
$(".input-item-count").each(function() {
$(".input-item-count").each(function () {
if ($(this).val() && $(this).val() !== "0") {
is_enabled = true;
}
@@ -185,18 +189,18 @@ $(function () {
// Invoice address form
$("input[data-required-if]").each(function () {
var dependent = $(this),
dependency = $($(this).attr("data-required-if")),
update = function (ev) {
var enabled = (dependency.attr("type") === 'checkbox' || dependency.attr("type") === 'radio') ? dependency.prop('checked') : !!dependency.val();
if (!dependent.is("[data-no-required-attr]")) {
dependent.prop('required', enabled);
}
dependent.closest('.form-group').toggleClass('required', enabled);
};
update();
dependency.closest('.form-group').find('input[name=' + dependency.attr("name") + ']').on("change", update);
dependency.closest('.form-group').find('input[name=' + dependency.attr("name") + ']').on("dp.change", update);
var dependent = $(this),
dependency = $($(this).attr("data-required-if")),
update = function (ev) {
var enabled = (dependency.attr("type") === 'checkbox' || dependency.attr("type") === 'radio') ? dependency.prop('checked') : !!dependency.val();
if (!dependent.is("[data-no-required-attr]")) {
dependent.prop('required', enabled);
}
dependent.closest('.form-group').toggleClass('required', enabled);
};
update();
dependency.closest('.form-group').find('input[name=' + dependency.attr("name") + ']').on("change", update);
dependency.closest('.form-group').find('input[name=' + dependency.attr("name") + ']').on("dp.change", update);
});
$("input[data-display-dependency]").each(function () {
@@ -219,16 +223,16 @@ $(function () {
dependency.closest('.form-group').find('input[name=' + dependency.attr("name") + ']').on("dp.change", update);
});
form_handlers($("body"));
form_handlers($("body"));
// Lightbox
lightbox.init();
});
function copy_answers(idx) {
var elements = $('*[data-idx="'+idx+'"] input, *[data-idx="'+idx+'"] select, *[data-idx="'+idx+'"] textarea');
function copy_answers(idx) {
var elements = $('*[data-idx="' + idx + '"] input, *[data-idx="' + idx + '"] select, *[data-idx="' + idx + '"] textarea');
var firstAnswers = $('*[data-idx="0"] input, *[data-idx="0"] select, *[data-idx="0"] textarea');
elements.each(function(index){
elements.each(function (index) {
var input = $(this),
tagName = input.prop('tagName').toLowerCase(),
attributeType = input.attr('type'),
@@ -250,14 +254,20 @@ function copy_answers(idx) {
break;
case "checkbox":
case "radio":
input.prop("checked", firstAnswers.filter("[name$=" + suffix + "]").prop("checked"));
if (input.attr('value')) {
input.prop("checked", firstAnswers.filter("[name$=" + suffix + "][value=" + input.attr('value') + "]").prop("checked"));
} else {
input.prop("checked", firstAnswers.filter("[name$=" + suffix + "]").prop("checked"));
}
break;
default:
input.val(firstAnswers.filter("[name$=" + suffix + "]").val());
}
}
break;
default:
input.val(firstAnswers.filter("[name$=" + suffix + "]").val());
}
}
});
questions_toggle_dependent(true);
}

View File

@@ -0,0 +1,71 @@
/*global $ */
function questions_toggle_dependent(ev) {
function q_should_be_shown($el) {
if (!$el.attr('data-question-dependency')) {
return true;
}
var dependency_name = $el.attr("name").split("_")[0] + "_" + $el.attr("data-question-dependency");
var dependency_value = $el.attr("data-question-dependency-value");
var $dependency_el;
if ($("select[name=" + dependency_name + "]").length) {
// dependency is type C
$dependency_el = $("select[name=" + dependency_name + "]");
if (!$dependency_el.closest(".form-group").hasClass("dependency-hidden")) { // do not show things that depend on hidden things
return q_should_be_shown($dependency_el) && $dependency_el.val() === dependency_value;
}
} else if ($("input[type=checkbox][name=" + dependency_name + "]").length) {
// dependency type is B or M
if (dependency_value === "True" || dependency_value === "False") {
$dependency_el = $("input[name=" + dependency_name + "]");
if (!$dependency_el.closest(".form-group").hasClass("dependency-hidden")) { // do not show things that depend on hidden things
if (dependency_value === "True") {
return q_should_be_shown($dependency_el) && $dependency_el.prop('checked');
} else {
return q_should_be_shown($dependency_el) && !$dependency_el.prop('checked');
}
}
} else {
$dependency_el = $("input[value=" + dependency_value + "][name=" + dependency_name + "]");
if (!$dependency_el.closest(".form-group").hasClass("dependency-hidden")) { // do not show things that depend on hidden things
return q_should_be_shown($dependency_el) && $dependency_el.prop('checked');
}
}
}
}
$("[data-question-dependency]").each(function () {
var $dependent = $(this).closest(".form-group");
var is_shown = !$dependent.hasClass("dependency-hidden");
var should_be_shown = q_should_be_shown($(this));
if (should_be_shown && !is_shown) {
$dependent.stop().removeClass("dependency-hidden");
if (!ev) {
$dependent.show();
} else {
$dependent.slideDown();
}
$dependent.find("input.required-hidden, select.required-hidden, textarea.required-hidden").each(function () {
$(this).prop("required", true).removeClass("required-hidden");
});
} else if (!should_be_shown && is_shown) {
if ($dependent.hasClass("has-error") || $dependent.find(".has-error").length) {
// Do not hide things with invalid validation
return;
}
$dependent.stop().addClass("dependency-hidden");
if (!ev) {
$dependent.hide();
} else {
$dependent.slideUp();
}
$dependent.find("input[required], select[required], textarea[required]").each(function () {
$(this).prop("required", false).addClass("required-hidden");
});
}
});
}