Merge pull request #7486 from tchak/feat-form-with-conditions
feat(dossier): toggle conditional champs visibility
This commit is contained in:
commit
b2c480fcdc
12 changed files with 67 additions and 24 deletions
|
@ -172,7 +172,7 @@ module Users
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render :brouillon }
|
format.html { render :brouillon }
|
||||||
format.turbo_stream
|
format.turbo_stream { render layout: false }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -89,8 +89,8 @@ class Champ < ApplicationRecord
|
||||||
@sections ||= dossier&.sections_for(self)
|
@sections ||= dossier&.sections_for(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def mandatory_and_blank?
|
def mandatory_blank_and_visible?
|
||||||
mandatory? && blank?
|
mandatory? && blank? && visible?
|
||||||
end
|
end
|
||||||
|
|
||||||
def blank?
|
def blank?
|
||||||
|
@ -180,8 +180,24 @@ class Champ < ApplicationRecord
|
||||||
raise NotImplemented.new(:fetch_external_data)
|
raise NotImplemented.new(:fetch_external_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def conditional?
|
||||||
|
type_de_champ.condition.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def visible?
|
||||||
|
if conditional?
|
||||||
|
type_de_champ.condition.compute(champs_for_condition)
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def champs_for_condition
|
||||||
|
private? ? dossier.champs_private : dossier.champs
|
||||||
|
end
|
||||||
|
|
||||||
def html_id
|
def html_id
|
||||||
"#{stable_id}-#{id}"
|
"#{stable_id}-#{id}"
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,8 +38,8 @@ class Champs::PieceJustificativeChamp < Champ
|
||||||
# We don’t know how to search inside documents yet
|
# We don’t know how to search inside documents yet
|
||||||
end
|
end
|
||||||
|
|
||||||
def mandatory_and_blank?
|
def mandatory_blank_and_visible?
|
||||||
mandatory? && !piece_justificative_file.attached?
|
mandatory? && !piece_justificative_file.attached? && visible?
|
||||||
end
|
end
|
||||||
|
|
||||||
def for_export
|
def for_export
|
||||||
|
|
|
@ -24,7 +24,7 @@ class Champs::SiretChamp < Champ
|
||||||
etablissement.present? ? etablissement.search_terms : [value]
|
etablissement.present? ? etablissement.search_terms : [value]
|
||||||
end
|
end
|
||||||
|
|
||||||
def mandatory_and_blank?
|
def mandatory_blank_and_visible?
|
||||||
mandatory? && Siret.new(siret: value).invalid?
|
mandatory? && Siret.new(siret: value).invalid? && visible?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,8 +32,8 @@ class Champs::TitreIdentiteChamp < Champ
|
||||||
# We don’t know how to search inside documents yet
|
# We don’t know how to search inside documents yet
|
||||||
end
|
end
|
||||||
|
|
||||||
def mandatory_and_blank?
|
def mandatory_blank_and_visible?
|
||||||
mandatory? && !piece_justificative_file.attached?
|
mandatory? && !piece_justificative_file.attached? && visible?
|
||||||
end
|
end
|
||||||
|
|
||||||
def for_export
|
def for_export
|
||||||
|
|
|
@ -1077,7 +1077,7 @@ class Dossier < ApplicationRecord
|
||||||
|
|
||||||
def check_mandatory_champs
|
def check_mandatory_champs
|
||||||
(champs + champs.filter(&:repetition?).flat_map(&:champs))
|
(champs + champs.filter(&:repetition?).flat_map(&:champs))
|
||||||
.filter(&:mandatory_and_blank?)
|
.filter(&:mandatory_blank_and_visible?)
|
||||||
.map do |champ|
|
.map do |champ|
|
||||||
"Le champ #{champ.libelle.truncate(200)} doit être rempli."
|
"Le champ #{champ.libelle.truncate(200)} doit être rempli."
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
- champs.reject(&:exclude_from_view?).each do |c|
|
- champs.reject(&:exclude_from_view?).filter(&:visible?).each do |c|
|
||||||
- if c.type_champ == TypeDeChamp.type_champs.fetch(:repetition)
|
- if c.type_champ == TypeDeChamp.type_champs.fetch(:repetition)
|
||||||
%tr
|
%tr
|
||||||
%td.libelle.repetition{ colspan: 3 }
|
%td.libelle.repetition{ colspan: 3 }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.editable-champ{ class: "editable-champ-#{champ.type_champ}", id: champ.input_group_id, data: autosave_available?(champ) ? { controller: 'autosave' } : {} }
|
.editable-champ{ class: "editable-champ-#{champ.type_champ} #{champ.visible? ? '' : 'hidden'}", id: champ.input_group_id, data: autosave_available?(champ) ? { controller: 'autosave' } : {} }
|
||||||
- if champ.repetition?
|
- if champ.repetition?
|
||||||
%h3.header-subsection= champ.libelle
|
%h3.header-subsection= champ.libelle
|
||||||
- if champ.description.present?
|
- if champ.description.present?
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
- @dossier.champs.filter(&:conditional?).each do |champ|
|
||||||
|
- if champ.visible?
|
||||||
|
= turbo_stream.show champ.input_group_id
|
||||||
|
- else
|
||||||
|
= turbo_stream.hide champ.input_group_id
|
|
@ -1,26 +1,26 @@
|
||||||
shared_examples 'champ_spec' do
|
shared_examples 'champ_spec' do
|
||||||
describe 'mandatory_and_blank?' do
|
describe 'mandatory_blank_and_visible?' do
|
||||||
let(:type_de_champ) { build(:type_de_champ, mandatory: mandatory) }
|
let(:type_de_champ) { build(:type_de_champ, mandatory: mandatory) }
|
||||||
let(:champ) { build(:champ, type_de_champ: type_de_champ, value: value) }
|
let(:champ) { build(:champ, type_de_champ: type_de_champ, value: value) }
|
||||||
let(:value) { '' }
|
let(:value) { '' }
|
||||||
let(:mandatory) { true }
|
let(:mandatory) { true }
|
||||||
|
|
||||||
context 'when mandatory and blank' do
|
context 'when mandatory and blank' do
|
||||||
it { expect(champ.mandatory_and_blank?).to be(true) }
|
it { expect(champ.mandatory_blank_and_visible?).to be(true) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when carte mandatory and blank' do
|
context 'when carte mandatory and blank' do
|
||||||
let(:type_de_champ) { build(:type_de_champ_carte, mandatory: mandatory) }
|
let(:type_de_champ) { build(:type_de_champ_carte, mandatory: mandatory) }
|
||||||
let(:champ) { build(:champ_carte, type_de_champ: type_de_champ, value: value) }
|
let(:champ) { build(:champ_carte, type_de_champ: type_de_champ, value: value) }
|
||||||
let(:value) { nil }
|
let(:value) { nil }
|
||||||
it { expect(champ.mandatory_and_blank?).to be(true) }
|
it { expect(champ.mandatory_blank_and_visible?).to be(true) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when multiple_drop_down_list mandatory and blank' do
|
context 'when multiple_drop_down_list mandatory and blank' do
|
||||||
let(:type_de_champ) { build(:type_de_champ_multiple_drop_down_list, mandatory: mandatory) }
|
let(:type_de_champ) { build(:type_de_champ_multiple_drop_down_list, mandatory: mandatory) }
|
||||||
let(:champ) { build(:champ_multiple_drop_down_list, type_de_champ: type_de_champ, value: value) }
|
let(:champ) { build(:champ_multiple_drop_down_list, type_de_champ: type_de_champ, value: value) }
|
||||||
let(:value) { '[]' }
|
let(:value) { '[]' }
|
||||||
it { expect(champ.mandatory_and_blank?).to be(true) }
|
it { expect(champ.mandatory_blank_and_visible?).to be(true) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when repetition blank' do
|
context 'when repetition blank' do
|
||||||
|
@ -39,18 +39,18 @@ shared_examples 'champ_spec' do
|
||||||
|
|
||||||
context 'when not blank' do
|
context 'when not blank' do
|
||||||
let(:value) { 'yop' }
|
let(:value) { 'yop' }
|
||||||
it { expect(champ.mandatory_and_blank?).to be(false) }
|
it { expect(champ.mandatory_blank_and_visible?).to be(false) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when not mandatory' do
|
context 'when not mandatory' do
|
||||||
let(:mandatory) { false }
|
let(:mandatory) { false }
|
||||||
it { expect(champ.mandatory_and_blank?).to be(false) }
|
it { expect(champ.mandatory_blank_and_visible?).to be(false) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when not mandatory or blank' do
|
context 'when not mandatory or blank' do
|
||||||
let(:value) { 'u' }
|
let(:value) { 'u' }
|
||||||
let(:mandatory) { false }
|
let(:mandatory) { false }
|
||||||
it { expect(champ.mandatory_and_blank?).to be(false) }
|
it { expect(champ.mandatory_blank_and_visible?).to be(false) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ describe Champs::LinkedDropDownListChamp do
|
||||||
let(:type_de_champ) { build(:type_de_champ_linked_drop_down_list, drop_down_list_value: value) }
|
let(:type_de_champ) { build(:type_de_champ_linked_drop_down_list, drop_down_list_value: value) }
|
||||||
|
|
||||||
it 'blank is fine' do
|
it 'blank is fine' do
|
||||||
is_expected.not_to be_mandatory_and_blank
|
is_expected.not_to be_mandatory_blank_and_visible
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -86,27 +86,27 @@ describe Champs::LinkedDropDownListChamp do
|
||||||
let(:type_de_champ) { build(:type_de_champ_linked_drop_down_list, mandatory: true, drop_down_list_value: value) }
|
let(:type_de_champ) { build(:type_de_champ_linked_drop_down_list, mandatory: true, drop_down_list_value: value) }
|
||||||
|
|
||||||
context 'when there is no value' do
|
context 'when there is no value' do
|
||||||
it { is_expected.to be_mandatory_and_blank }
|
it { is_expected.to be_mandatory_blank_and_visible }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there is a primary value' do
|
context 'when there is a primary value' do
|
||||||
before { subject.primary_value = 'Primary' }
|
before { subject.primary_value = 'Primary' }
|
||||||
|
|
||||||
context 'when there is no secondary value' do
|
context 'when there is no secondary value' do
|
||||||
it { is_expected.to be_mandatory_and_blank }
|
it { is_expected.to be_mandatory_blank_and_visible }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there is a secondary value' do
|
context 'when there is a secondary value' do
|
||||||
before { subject.secondary_value = 'Secondary' }
|
before { subject.secondary_value = 'Secondary' }
|
||||||
|
|
||||||
it { is_expected.not_to be_mandatory_and_blank }
|
it { is_expected.not_to be_mandatory_blank_and_visible }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there is nothing to select for the secondary value' do
|
context 'when there is nothing to select for the secondary value' do
|
||||||
let(:value) { "--A--\nAbbott\nAbelard\n--B--\n--C--\nCynthia" }
|
let(:value) { "--A--\nAbbott\nAbelard\n--B--\n--C--\nCynthia" }
|
||||||
before { subject.primary_value = 'B' }
|
before { subject.primary_value = 'B' }
|
||||||
|
|
||||||
it { is_expected.not_to be_mandatory_and_blank }
|
it { is_expected.not_to be_mandatory_blank_and_visible }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -271,6 +271,28 @@ describe 'The user' do
|
||||||
expect(page).to have_text('file.pdf')
|
expect(page).to have_text('file.pdf')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with condition' do
|
||||||
|
include Logic
|
||||||
|
|
||||||
|
let(:procedure) { create(:procedure, :published, :for_individual, :with_yes_no, :with_type_de_champ) }
|
||||||
|
let(:revision) { procedure.published_revision }
|
||||||
|
let(:yes_no_type_de_champ) { revision.types_de_champ.first }
|
||||||
|
let(:type_de_champ) { revision.types_de_champ.last }
|
||||||
|
let(:condition) { ds_eq(champ_value(yes_no_type_de_champ.stable_id), constant(true)) }
|
||||||
|
|
||||||
|
before { type_de_champ.update(condition: condition) }
|
||||||
|
|
||||||
|
scenario 'fill a dossier', js: true do
|
||||||
|
log_in(user, procedure)
|
||||||
|
|
||||||
|
fill_individual
|
||||||
|
|
||||||
|
expect(page).to have_field(type_de_champ.libelle, with: '', visible: false)
|
||||||
|
choose('Oui')
|
||||||
|
expect(page).to have_field(type_de_champ.libelle, with: '', visible: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'draft autosave' do
|
context 'draft autosave' do
|
||||||
scenario 'autosave a draft', js: true do
|
scenario 'autosave a draft', js: true do
|
||||||
log_in(user, simple_procedure)
|
log_in(user, simple_procedure)
|
||||||
|
|
Loading…
Reference in a new issue