decidim-forms 0.31.2 → 0.31.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 533c98f5bea9b27f56b1b38e31f566288c01c47d811c5ccd3b7ede29743162d5
4
- data.tar.gz: b5f8f7382b27174aec69cbbef6f114ebe11d72d0e3db13d51037d369f3dd404f
3
+ metadata.gz: 181f8efb5bbf6cad3d063b4b9db3f0ddf0092a89677a98a8764c787ba7e72cb6
4
+ data.tar.gz: b54651be0bde86cb03f32c34e1f435f3e98fea0f77b7d06ee89416dcd9b1858f
5
5
  SHA512:
6
- metadata.gz: a041e17927a8135ccaf698d1572598969e5998f3627a24b08291cc495672fb46a4238e42e70a4e06a04a5431a319f0781b2a8ddb6704e47358e2a4b8e5a1029b
7
- data.tar.gz: dac22d94c860656ba92a5461bb594a819a924627df74f3f654739fae8c529ff5e1a44979e7ae031bb08135273e03ccd21f93287aed5164d7fe198132674809ca
6
+ metadata.gz: baca11153797c7ef3247ef82a3ab2040e21d7dce54fc1b755d35126640d56971ff9d21bd2377b70b1d83decb887300fb98a22f2a503254d8a9d0521bda25b2fb
7
+ data.tar.gz: e1bf97e034df11d10fab37347e12858b46f52eefadf3ac4c5124a503950a96fbd4afa4a462fa528c6b0b420180bae40ba129ee08920cad3827ed7d3fe8dd593d
@@ -48,6 +48,11 @@ module Decidim
48
48
  Decidim::Forms::QuestionnaireParticipants.new(self).count_participants
49
49
  end
50
50
 
51
+ # Public: Returns only the actual question types (excludes separators and title_and_description)
52
+ def question_types
53
+ questions.not_separator.not_title_and_description
54
+ end
55
+
51
56
  private
52
57
 
53
58
  # salt is used to generate secure hash in anonymous responses
@@ -9,6 +9,7 @@ import AutoButtonsByPositionComponent from "src/decidim/admin/auto_buttons_by_po
9
9
  import AutoLabelByPositionComponent from "src/decidim/admin/auto_label_by_position.component"
10
10
  import createDynamicFields from "src/decidim/admin/dynamic_fields.component"
11
11
  import createFieldDependentInputs from "src/decidim/admin/field_dependent_inputs.component"
12
+ import sortable from "html5sortable/dist/html5sortable.es"
12
13
 
13
14
  export default function createEditableForm() {
14
15
  const wrapperSelector = ".questionnaire-questions";
@@ -22,6 +23,7 @@ export default function createEditableForm() {
22
23
  const matrixRowRemoveFieldButtonSelector = ".remove-matrix-row";
23
24
  const addMatrixRowButtonSelector = ".add-matrix-row";
24
25
  const maxChoicesWrapperSelector = ".questionnaire-question-max-choices";
26
+ const responseOptionFreeTextSelector = ".questionnaire-question-response-option-free-text";
25
27
 
26
28
  const displayConditionFieldSelector = ".questionnaire-question-display-condition";
27
29
  const displayConditionsWrapperSelector = ".questionnaire-question-display-conditions";
@@ -335,7 +337,10 @@ export default function createEditableForm() {
335
337
  const dynamicFieldsMatrixRows = dynamicFieldsForMatrixRows[fieldId];
336
338
 
337
339
  const onQuestionTypeChange = () => {
338
- if (isMultipleChoiceOption($fieldQuestionTypeSelect.val())) {
340
+ const $currentField = $fieldQuestionTypeSelect.parents(fieldSelector);
341
+ const questionType = $fieldQuestionTypeSelect.val();
342
+
343
+ if (isMultipleChoiceOption(questionType)) {
339
344
  const nOptions = $fieldQuestionTypeSelect.parents(fieldSelector).find(responseOptionFieldSelector).length;
340
345
 
341
346
  if (nOptions === 0) {
@@ -352,6 +357,12 @@ export default function createEditableForm() {
352
357
  dynamicFieldsMatrixRows._addField();
353
358
  }
354
359
  }
360
+
361
+ if (questionType === "sorting") {
362
+ $currentField.find(responseOptionFreeTextSelector).addClass("hidden");
363
+ } else {
364
+ $currentField.find(responseOptionFreeTextSelector).removeClass("hidden");
365
+ }
355
366
  };
356
367
 
357
368
  $fieldQuestionTypeSelect.on("change", onQuestionTypeChange);
@@ -396,6 +407,15 @@ export default function createEditableForm() {
396
407
  });
397
408
  }
398
409
 
410
+ const sortableContainer = document.querySelector(".questionnaire-questions-list[data-draggable-table]");
411
+ if (sortableContainer) {
412
+ sortable(sortableContainer, {
413
+ forcePlaceholderSize: true,
414
+ items: ".questionnaire-question",
415
+ handle: sortableContainer.dataset.draggableHandle || ".card-divider"
416
+ });
417
+ }
418
+
399
419
  // instead of initialize specific stuff, we send an event, with the DOM fragment we wanna update/refresh/bind
400
420
  document.dispatchEvent(new CustomEvent("ajax:loaded", { detail: $field[0] }));
401
421
  },
@@ -22,7 +22,7 @@
22
22
  }
23
23
 
24
24
  &__question-label {
25
- @apply text-black text-xl font-semibold relative before:content-[attr(data-response-idx)] before:w-6 before:h-6 md:before:absolute md:before:-left-4 md:before:-translate-x-full before:inline-flex before:justify-center before:rounded-full before:bg-background before:text-lg before:text-gray-2 before:font-semibold;
25
+ @apply text-black text-xl font-semibold;
26
26
  }
27
27
 
28
28
  &__question-description {
@@ -2,11 +2,11 @@
2
2
  <% is_expanded = question.errors.any? %>
3
3
  <div class="card questionnaire-question" id="accordion-<%= id %>-field">
4
4
  <div class="form__wrapper" data-controller="accordion">
5
- <div class="card-divider">
5
+ <%= content_tag :div, class: ["card-divider", ("hover:cursor-grab" if editable)] do %>
6
6
  <h2 class="card-title flex items-center">
7
7
  <span>
8
8
  <% if editable %>
9
- <%== icon("drag-move-2-fill") %>
9
+ <%= icon("draggable", class: "dragger") %>
10
10
  <% end %>
11
11
  <%= dynamic_title(translated_attribute(question.body), class: "question-title-statement", max_length: 50, omission: "...", placeholder: t(".question")) %>
12
12
  </span>
@@ -23,14 +23,14 @@
23
23
  </button>
24
24
 
25
25
  <% if editable %>
26
- <button class="button button__xs button__transparent-secondary small alert remove-question button--title">
26
+ <button type="button" class="button button__xs button__transparent-secondary small alert remove-question button--title">
27
27
  <%= icon("delete-bin-line") %>
28
28
  <span class="hidden md:block"><%= t(".remove") %></span>
29
29
  </button>
30
30
  <% end %>
31
31
  </span>
32
32
  </h2>
33
- </div>
33
+ <% end %>
34
34
 
35
35
  <div id="panel-<%= id %>-question-card" class="panel-question-card card-section collapsible <%= "panel-error" if is_expanded %>" aria-hidden="<%= is_expanded ? "false" : "true" %>">
36
36
  <div class="row column">
@@ -40,7 +40,15 @@
40
40
  <%= cell("decidim/announcement", t(".already_responded_warning"), callout_class: "warning" ) %>
41
41
  <% end %>
42
42
 
43
- <div class="questionnaire-questions-list flex flex-col py-6 gap-6 last:pb-0" data-draggable-table data-sort-url="#" data-draggable-handle=".card-divider" id="questionnaire-questions-list">
43
+ <% list_attributes = {
44
+ class: "questionnaire-questions-list flex flex-col py-6 gap-6 last:pb-0",
45
+ id: "questionnaire-questions-list"
46
+ }
47
+ list_attributes[:"data-draggable-table"] = true if questionnaire.questions_editable?
48
+ list_attributes[:"data-sort-url"] = "#" if questionnaire.questions_editable?
49
+ list_attributes[:"data-draggable-handle"] = ".card-divider" if questionnaire.questions_editable? %>
50
+
51
+ <%= content_tag :div, list_attributes do %>
44
52
  <% @form.questions.each_with_index do |question, index| %>
45
53
  <%= fields_for "questions[questions][]", question do |question_form| %>
46
54
  <% if question.separator? %>
@@ -67,7 +75,7 @@
67
75
  <% end %>
68
76
  <% end %>
69
77
  <% end %>
70
- </div>
78
+ <% end %>
71
79
  </div>
72
80
 
73
81
  <%= append_javascript_pack_tag "decidim_forms_admin" %>
@@ -26,15 +26,15 @@
26
26
  %>
27
27
  </div>
28
28
 
29
- <div class="row column">
30
- <%=
31
- form.check_box(
32
- :free_text,
33
- label: t(".free_text"),
34
- disabled: !questionnaire.questions_editable?
35
- )
36
- %>
37
- </div>
29
+ <div class="row column questionnaire-question-response-option-free-text">
30
+ <%=
31
+ form.check_box(
32
+ :free_text,
33
+ label: t(".free_text"),
34
+ disabled: !questionnaire.questions_editable?
35
+ )
36
+ %>
37
+ </div>
38
38
  </div>
39
39
 
40
40
  <% if response_option.persisted? %>
@@ -1,18 +1,18 @@
1
1
  <% question = form.object %>
2
2
 
3
3
  <div class="card questionnaire-question" id="<%= id %>-field">
4
- <div class="card-divider">
4
+ <%= content_tag :div, class: ["card-divider", ("hover:cursor-grab" if editable)] do %>
5
5
  <h2 class="card-title flex items-center">
6
6
  <span>
7
7
  <% if editable %>
8
- <%== icon("drag-move-2-fill") %>
8
+ <%= icon("draggable", class: "dragger") %>
9
9
  <% end %>
10
10
  <%= dynamic_title(t(".separator"), class: "question-title-statement", max_length: 50, omission: "...", placeholder: t(".separator")) %>
11
11
  </span>
12
12
 
13
13
  <div class="flex flex-row-reverse items-center gap-x-4 ml-auto">
14
14
  <% if editable %>
15
- <button class="button button__xs button__transparent button__transparent-secondary small alert remove-question button--title">
15
+ <button type="button" class="button button__xs button__transparent button__transparent-secondary small alert remove-question button--title">
16
16
  <%= icon("delete-bin-line") %>
17
17
  <span class="hidden md:block"><%= t(".remove") %></span>
18
18
  </button>
@@ -32,5 +32,5 @@
32
32
 
33
33
  <%= form.hidden_field :position, value: question.position || 0, disabled: !editable %>
34
34
  <%= form.hidden_field :deleted, disabled: !editable %>
35
- </div>
35
+ <% end %>
36
36
  </div>
@@ -3,11 +3,11 @@
3
3
 
4
4
  <div class="card questionnaire-question" id="accordion-<%= id %>-field">
5
5
  <div data-controller="accordion">
6
- <div class="card-divider">
6
+ <%= content_tag :div, class: ["card-divider", ("hover:cursor-grab" if editable)] do %>
7
7
  <h2 class="card-title flex items-center">
8
8
  <span>
9
9
  <% if editable %>
10
- <%== icon("drag-move-2-fill") %>
10
+ <%= icon("draggable", class: "dragger") %>
11
11
  <% end %>
12
12
  <%= dynamic_title(translated_attribute(question.body), class: "question-title-statement", max_length: 50, omission: "...", placeholder: t(".title_and_description")) %>
13
13
  </span>
@@ -23,7 +23,7 @@
23
23
  </button>
24
24
 
25
25
  <% if editable %>
26
- <button class="button button__xs button__transparent button__transparent-secondary small alert remove-question button--title">
26
+ <button type="button" class="button button__xs button__transparent button__transparent-secondary small alert remove-question button--title">
27
27
  <%= icon("delete-bin-line") %>
28
28
  <span class="hidden md:block"><%= t(".remove") %></span>
29
29
  </button>
@@ -71,6 +71,6 @@
71
71
 
72
72
  <%= form.hidden_field :position, value: question.position || 0, disabled: !editable %>
73
73
  <%= form.hidden_field :deleted, disabled: !editable %>
74
- </div>
74
+ <% end %>
75
75
  </div>
76
76
  </div>
@@ -17,7 +17,6 @@
17
17
 
18
18
  <%= invisible_captcha %>
19
19
  <% response_idx = 0 %>
20
- <% cleaned_response_idx = 1 %>
21
20
  <% @form.responses_by_step.each_with_index do |step_responses, step_index| %>
22
21
  <div id="step-<%= step_index %>" class="response-questionnaire__step" <%= "hidden" if !step_index.zero? %>>
23
22
 
@@ -39,16 +38,11 @@
39
38
  response_form:,
40
39
  response:,
41
40
  response_idx:,
42
- cleaned_response_idx:,
43
41
  disabled: !current_participatory_space.can_participate?(current_user)
44
42
  ) %>
45
43
  <% end %>
46
44
  </div>
47
45
 
48
- <% if !(response.question.separator? || response.question.title_and_description?) %>
49
- <% cleaned_response_idx += 1 %>
50
- <% end %>
51
-
52
46
  <% response_idx += 1 %>
53
47
 
54
48
  <% end %>
@@ -24,8 +24,7 @@
24
24
  <div class="response-questionnaire__question">
25
25
  <%
26
26
  label_options = {
27
- class: "response-questionnaire__question-label questionnaire-question",
28
- data: { "response-idx": cleaned_response_idx }
27
+ class: "response-questionnaire__question-label questionnaire-question"
29
28
  }
30
29
  label_options[:for] = nil if %w(matrix_single matrix_multiple single_option multiple_option).include?(response.question.question_type)
31
30
  %>
@@ -50,7 +50,7 @@ ca-IT:
50
50
  description: Descripció
51
51
  ends_at: Respostes acceptades fins a
52
52
  ends_at_help: Deixar en blanc si no hi ha cap data específica
53
- starts_at: Respostes acceptades fins a
53
+ starts_at: Respostes acceptades des de
54
54
  starts_at_help: Deixar en blanc si no hi ha cap data específica
55
55
  tos: Termes del servei
56
56
  questionnaires:
@@ -50,7 +50,7 @@ ca:
50
50
  description: Descripció
51
51
  ends_at: Respostes acceptades fins a
52
52
  ends_at_help: Deixar en blanc si no hi ha cap data específica
53
- starts_at: Respostes acceptades fins a
53
+ starts_at: Respostes acceptades des de
54
54
  starts_at_help: Deixar en blanc si no hi ha cap data específica
55
55
  tos: Termes del servei
56
56
  questionnaires:
@@ -50,7 +50,7 @@ es-MX:
50
50
  description: Descripción
51
51
  ends_at: Respuestas aceptadas hasta
52
52
  ends_at_help: Dejar en blanco si no hay ninguna fecha específica
53
- starts_at: Respuestas aceptadas hasta
53
+ starts_at: Respuestas aceptadas desde
54
54
  starts_at_help: Dejar en blanco si no hay ninguna fecha específica
55
55
  tos: Términos y condiciones de uso
56
56
  questionnaires:
@@ -50,7 +50,7 @@ es-PY:
50
50
  description: Descripción
51
51
  ends_at: Respuestas aceptadas hasta
52
52
  ends_at_help: Dejar en blanco si no hay ninguna fecha específica
53
- starts_at: Respuestas aceptadas hasta
53
+ starts_at: Respuestas aceptadas desde
54
54
  starts_at_help: Dejar en blanco si no hay ninguna fecha específica
55
55
  tos: Términos y condiciones de uso
56
56
  questionnaires:
@@ -50,7 +50,7 @@ es:
50
50
  description: Descripción
51
51
  ends_at: Respuestas aceptadas hasta
52
52
  ends_at_help: Dejar en blanco si no hay ninguna fecha específica
53
- starts_at: Respuestas aceptadas hasta
53
+ starts_at: Respuestas aceptadas desde
54
54
  starts_at_help: Dejar en blanco si no hay ninguna fecha específica
55
55
  tos: Términos y condiciones de uso
56
56
  questionnaires:
@@ -192,9 +192,7 @@ shared_examples_for "has questionnaire" do
192
192
 
193
193
  expect(form_fields[0]).to have_i18n_content(question.body)
194
194
  expect(form_fields[1]).to have_i18n_content(other_question.body)
195
- 2.times do |index|
196
- expect(form_fields[index]).to have_css("[data-response-idx='#{index + 1}']")
197
- end
195
+ expect(page.text.index(translated_attribute(other_question.body))).to be > page.text.index(translated_attribute(question.body))
198
196
  end
199
197
  end
200
198
 
@@ -412,11 +412,57 @@ shared_examples_for "add questions" do
412
412
 
413
413
  select "Single option", from: "Type"
414
414
  expect(page).to have_css("input[type=checkbox][id$=_free_text]")
415
+
416
+ select "Sorting", from: "Type"
417
+ expect(page).to have_no_css("input[type=checkbox][id$=_free_text]", visible: :visible)
415
418
  end
416
419
 
417
420
  it_behaves_like "updating the max choices selector according to the configured options"
418
421
  end
419
422
 
423
+ context "when adding a sorting question" do
424
+ before do
425
+ click_on "Add question"
426
+
427
+ expand_all_questions
428
+
429
+ within ".questionnaire-question" do
430
+ fill_in find_nested_form_field_locator("body_en"), with: "This is a sorting question"
431
+ select "Single option", from: "Type"
432
+ end
433
+ end
434
+
435
+ it "does not display the free text option when switching to sorting type" do
436
+ within ".questionnaire-question" do
437
+ expect(page).to have_css("input[type=checkbox][id$=_free_text]")
438
+
439
+ select "Sorting", from: "Type"
440
+
441
+ expect(page).to have_no_css("input[type=checkbox][id$=_free_text]", visible: :visible)
442
+ end
443
+ end
444
+
445
+ it "shows the free text option when switching back from sorting to single option" do
446
+ within ".questionnaire-question" do
447
+ select "Sorting", from: "Type"
448
+ expect(page).to have_no_css("input[type=checkbox][id$=_free_text]", visible: :visible)
449
+
450
+ select "Single option", from: "Type"
451
+ expect(page).to have_css("input[type=checkbox][id$=_free_text]")
452
+ end
453
+ end
454
+
455
+ it "hides free text option when switching from multiple option to sorting" do
456
+ within ".questionnaire-question" do
457
+ select "Multiple option", from: "Type"
458
+ expect(page).to have_css("input[type=checkbox][id$=_free_text]")
459
+
460
+ select "Sorting", from: "Type"
461
+ expect(page).to have_no_css("input[type=checkbox][id$=_free_text]", visible: :visible)
462
+ end
463
+ end
464
+ end
465
+
420
466
  context "when adding a matrix question" do
421
467
  let(:multiple_option_string) { "Matrix (Multiple option)" }
422
468
  let(:single_option_string) { "Matrix (Single option)" }
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ shared_examples_for "manage questionnaire draggable behavior" do
6
+ let!(:question) { create(:questionnaire_question, body:, questionnaire:) }
7
+
8
+ context "when questionnaire has no responses (editable)" do
9
+ before do
10
+ visit current_path
11
+ end
12
+
13
+ it "shows draggable data attributes for questions list" do
14
+ expect(page).to have_css("[data-draggable-table]")
15
+ expect(page).to have_css("[data-draggable-handle]")
16
+ end
17
+
18
+ describe "when hovering over card divider" do
19
+ it "shows resize cursor for editable questions" do
20
+ within first(".questionnaire-question") do
21
+ expect(page).to have_css(".card-divider.hover\\:cursor-grab")
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ context "when questionnaire has responses (not editable)" do
28
+ let!(:response) { create(:response, question:, questionnaire:) }
29
+
30
+ before do
31
+ visit current_path
32
+ end
33
+
34
+ it "does not show draggable data attributes for questions list" do
35
+ expect(page).to have_no_css("[data-draggable-table]")
36
+ expect(page).to have_no_css("[data-draggable-handle]")
37
+ end
38
+
39
+ describe "when hovering over card divider" do
40
+ it "does not show resize cursor for non-editable questions" do
41
+ within first(".questionnaire-question") do
42
+ expect(page).to have_no_css(".card-divider.hover\\:cursor-grab")
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -6,6 +6,7 @@ require "decidim/forms/test/shared_examples/manage_questionnaires/add_questions"
6
6
  require "decidim/forms/test/shared_examples/manage_questionnaires/update_questions"
7
7
  require "decidim/forms/test/shared_examples/manage_questionnaires/add_display_conditions"
8
8
  require "decidim/forms/test/shared_examples/manage_questionnaires/update_display_conditions"
9
+ require "decidim/forms/test/shared_examples/manage_questionnaires/draggable_behavior"
9
10
 
10
11
  shared_examples_for "manage questionnaires" do
11
12
  let(:body) do
@@ -33,6 +34,7 @@ shared_examples_for "manage questionnaires" do
33
34
  it_behaves_like "update questions"
34
35
  it_behaves_like "add display conditions"
35
36
  it_behaves_like "update display conditions"
37
+ it_behaves_like "manage questionnaire draggable behavior"
36
38
  end
37
39
 
38
40
  context "when the questionnaire is already responded" do
@@ -4,7 +4,7 @@ module Decidim
4
4
  # This holds the decidim-forms version.
5
5
  module Forms
6
6
  def self.version
7
- "0.31.2"
7
+ "0.31.3"
8
8
  end
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decidim-forms
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.31.2
4
+ version: 0.31.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josep Jaume Rey Peroy
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2026-02-26 00:00:00.000000000 Z
14
+ date: 2026-03-26 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: decidim-core
@@ -19,42 +19,42 @@ dependencies:
19
19
  requirements:
20
20
  - - '='
21
21
  - !ruby/object:Gem::Version
22
- version: 0.31.2
22
+ version: 0.31.3
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - '='
28
28
  - !ruby/object:Gem::Version
29
- version: 0.31.2
29
+ version: 0.31.3
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: decidim-admin
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  requirements:
34
34
  - - '='
35
35
  - !ruby/object:Gem::Version
36
- version: 0.31.2
36
+ version: 0.31.3
37
37
  type: :development
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
41
  - - '='
42
42
  - !ruby/object:Gem::Version
43
- version: 0.31.2
43
+ version: 0.31.3
44
44
  - !ruby/object:Gem::Dependency
45
45
  name: decidim-dev
46
46
  requirement: !ruby/object:Gem::Requirement
47
47
  requirements:
48
48
  - - '='
49
49
  - !ruby/object:Gem::Version
50
- version: 0.31.2
50
+ version: 0.31.3
51
51
  type: :development
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
54
54
  requirements:
55
55
  - - '='
56
56
  - !ruby/object:Gem::Version
57
- version: 0.31.2
57
+ version: 0.31.3
58
58
  description: A forms gem for decidim.
59
59
  email:
60
60
  - josepjaume@gmail.com
@@ -284,6 +284,7 @@ files:
284
284
  - lib/decidim/forms/test/shared_examples/manage_questionnaires.rb
285
285
  - lib/decidim/forms/test/shared_examples/manage_questionnaires/add_display_conditions.rb
286
286
  - lib/decidim/forms/test/shared_examples/manage_questionnaires/add_questions.rb
287
+ - lib/decidim/forms/test/shared_examples/manage_questionnaires/draggable_behavior.rb
287
288
  - lib/decidim/forms/test/shared_examples/manage_questionnaires/update_display_conditions.rb
288
289
  - lib/decidim/forms/test/shared_examples/manage_questionnaires/update_questions.rb
289
290
  - lib/decidim/forms/user_responses_serializer.rb