feat(dossier): multiselect without react
This commit is contained in:
parent
cd74ea46cc
commit
fce78bbf30
13 changed files with 104 additions and 50 deletions
|
@ -3,13 +3,12 @@
|
|||
= @form.collection_check_boxes(:value, @champ.enabled_non_empty_options, :to_s, :to_s) do |b|
|
||||
- tag.div(class: 'editable-champ editable-champ-checkbox') do
|
||||
- b.label(for: @champ.checkbox_id(b.value)) do
|
||||
- b.check_box({ multiple: true, checked: @champ&.selected_options&.include?(b.value), aria: { describedby: @champ.describedby_id }, id: @champ.checkbox_id(b.value) }) + b.text
|
||||
- b.check_box({ multiple: true, checked: @champ.selected_options.include?(b.value), aria: { describedby: @champ.describedby_id }, id: @champ.checkbox_id(b.value) }) + b.text
|
||||
- else
|
||||
= @form.hidden_field :value
|
||||
= react_component("ComboMultipleDropdownList",
|
||||
options: @champ.options,
|
||||
selected: @champ.selected_options,
|
||||
disabled: @champ.disabled_options,
|
||||
id: @champ.input_id,
|
||||
labelledby: @champ.labelledby_id,
|
||||
describedby: @champ.describedby_id)
|
||||
- if @champ.selected_options.present?
|
||||
.fr-mb-2w{ "data-turbo": "true" }
|
||||
- @champ.selected_options.each do |option|
|
||||
= render NestedForms::OwnedButtonComponent.new(formaction: champs_options_path(@champ.id, option:), http_method: :delete, opt: { class: 'fr-tag fr-tag--dismiss fr-mb-1w fr-mr-1w', id: @champ.checkbox_id(option) }) do
|
||||
= option
|
||||
- if @champ.unselected_options.present?
|
||||
= @form.select :value, @champ.unselected_options, { selected: '', include_blank: '' }, id: @champ.input_id, aria: { describedby: @champ.describedby_id }
|
||||
|
|
9
app/controllers/champs/options_controller.rb
Normal file
9
app/controllers/champs/options_controller.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
class Champs::OptionsController < ApplicationController
|
||||
before_action :authenticate_logged_user!
|
||||
|
||||
def remove
|
||||
@champ = policy_scope(Champ).includes(:champs).find(params[:champ_id])
|
||||
@next_checkbox_id = @champ.next_checkbox_id(params[:option])
|
||||
@champ.remove_option([params[:option]].compact)
|
||||
end
|
||||
end
|
|
@ -1,11 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import { groupId } from './shared/hooks';
|
||||
import ComboMultiple, { ComboMultipleProps } from './ComboMultiple';
|
||||
|
||||
export default function ComboMultipleDropdownList({
|
||||
id,
|
||||
...props
|
||||
}: ComboMultipleProps & { id: string }) {
|
||||
return <ComboMultiple id={id} {...props} group={groupId(id)} />;
|
||||
}
|
|
@ -21,8 +21,6 @@
|
|||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::MultipleDropDownListChamp < Champ
|
||||
before_save :format_before_save
|
||||
|
||||
validate :values_are_in_options, if: -> { value.present? }
|
||||
|
||||
def options?
|
||||
|
@ -84,23 +82,41 @@ class Champs::MultipleDropDownListChamp < Champ
|
|||
end
|
||||
|
||||
def checkbox_id(value)
|
||||
"#{input_id}-#{value.parameterize}"
|
||||
"#{input_id}-#{Digest::MD5.hexdigest(value)}"
|
||||
end
|
||||
|
||||
def next_checkbox_id(value)
|
||||
return nil if value.blank? || !selected_options.include?(value)
|
||||
index = selected_options.index(value)
|
||||
next_values = selected_options.reject { _1 == value }
|
||||
next_value = next_values[index] || next_values.last
|
||||
next_value ? checkbox_id(next_value) : nil
|
||||
end
|
||||
|
||||
def unselected_options
|
||||
enabled_non_empty_options - selected_options
|
||||
end
|
||||
|
||||
def value=(value)
|
||||
return super(nil) if value.blank?
|
||||
|
||||
values = if value.is_a?(Array)
|
||||
value
|
||||
elsif value.starts_with?('[')
|
||||
JSON.parse(value)
|
||||
else
|
||||
selected_options + [value]
|
||||
end.uniq.without('')
|
||||
|
||||
if values.empty?
|
||||
super(nil)
|
||||
else
|
||||
super(values.to_json)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def format_before_save
|
||||
if value.present?
|
||||
json = JSON.parse(value)
|
||||
if json == ['']
|
||||
self.value = nil
|
||||
else
|
||||
json = json - ['']
|
||||
self.value = json.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def values_are_in_options
|
||||
json = selected_options.reject(&:blank?)
|
||||
return if json.empty?
|
||||
|
|
|
@ -500,7 +500,9 @@ class TypeDeChamp < ApplicationRecord
|
|||
|
||||
def self.refresh_after_update?(type_champ)
|
||||
case type_champ
|
||||
when type_champs.fetch(:epci), type_champs.fetch(:communes)
|
||||
when type_champs.fetch(:epci),
|
||||
type_champs.fetch(:communes),
|
||||
type_champs.fetch(:multiple_drop_down_list)
|
||||
true
|
||||
else
|
||||
false
|
||||
|
|
8
app/views/champs/options/remove.turbo_stream.haml
Normal file
8
app/views/champs/options/remove.turbo_stream.haml
Normal file
|
@ -0,0 +1,8 @@
|
|||
= fields_for @champ.input_name, @champ do |form|
|
||||
= turbo_stream.replace @champ.input_group_id do
|
||||
= render EditableChamp::EditableChampComponent.new champ: @champ, form:
|
||||
|
||||
- if @next_checkbox_id.present?
|
||||
= turbo_stream.focus @next_checkbox_id
|
||||
- else
|
||||
= turbo_stream.focus @champ.input_id
|
|
@ -170,6 +170,7 @@ Rails.application.routes.draw do
|
|||
get ':champ_id/dossier_link', to: 'dossier_link#show', as: :dossier_link
|
||||
post ':champ_id/repetition', to: 'repetition#add', as: :repetition
|
||||
delete ':champ_id/repetition', to: 'repetition#remove'
|
||||
delete ':champ_id/options', to: 'options#remove', as: :options
|
||||
|
||||
get ':champ_id/carte/features', to: 'carte#index', as: :carte_features
|
||||
post ':champ_id/carte/features', to: 'carte#create'
|
||||
|
|
|
@ -824,7 +824,7 @@ describe Instructeurs::DossiersController, type: :controller do
|
|||
end
|
||||
|
||||
it {
|
||||
expect(champ_multiple_drop_down_list.value).to eq('["val1", "val2"]')
|
||||
expect(champ_multiple_drop_down_list.value).to eq('["val1","val2"]')
|
||||
expect(champ_linked_drop_down_list.primary_value).to eq('primary')
|
||||
expect(champ_linked_drop_down_list.secondary_value).to eq('secondary')
|
||||
expect(champ_datetime.value).to eq('2019-12-21T13:17:00+01:00')
|
||||
|
|
|
@ -117,7 +117,7 @@ describe Champ do
|
|||
# when using the old form, and the ChampsService Class
|
||||
# TODO: to remove
|
||||
context 'when the value is already deserialized' do
|
||||
let(:value) { '["val1", "val2"]' }
|
||||
let(:value) { '["val1","val2"]' }
|
||||
|
||||
it { expect(champ.value).to eq(value) }
|
||||
|
||||
|
@ -135,7 +135,7 @@ describe Champ do
|
|||
context 'when a choice is selected' do
|
||||
let(:value) { '["", "val1", "val2"]' }
|
||||
|
||||
it { expect(champ.value).to eq('["val1", "val2"]') }
|
||||
it { expect(champ.value).to eq('["val1","val2"]') }
|
||||
end
|
||||
|
||||
context 'when all choices are removed' do
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
describe Champs::MultipleDropDownListChamp do
|
||||
let(:type_de_champ) { build(:type_de_champ_multiple_drop_down_list, drop_down_list_value: "val1\r\nval2\r\nval3") }
|
||||
let(:value) { nil }
|
||||
subject { build(:champ_multiple_drop_down_list, type_de_champ:, value:) }
|
||||
|
||||
describe 'validations' do
|
||||
describe 'inclusion' do
|
||||
let(:type_de_champ) { build(:type_de_champ_multiple_drop_down_list, drop_down_list_value: "val1\r\nval2\r\nval3") }
|
||||
subject { build(:champ_multiple_drop_down_list, type_de_champ:, value:) }
|
||||
|
||||
context 'when the value is nil' do
|
||||
let(:value) { nil }
|
||||
|
||||
it { is_expected.to be_valid }
|
||||
end
|
||||
|
||||
|
@ -35,4 +34,28 @@ describe Champs::MultipleDropDownListChamp do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#next_checkbox_id' do
|
||||
let(:value) { ["val1", "val2", "val3"] }
|
||||
|
||||
context 'when the value has next value' do
|
||||
it {
|
||||
expect(subject.next_checkbox_id("val1")).to eq(subject.checkbox_id("val2"))
|
||||
expect(subject.next_checkbox_id("val2")).to eq(subject.checkbox_id("val3"))
|
||||
}
|
||||
end
|
||||
|
||||
context 'when the value is last' do
|
||||
it { expect(subject.next_checkbox_id("val3")).to eq(subject.checkbox_id("val2")) }
|
||||
end
|
||||
|
||||
context 'when the value is invalid' do
|
||||
it { expect(subject.next_checkbox_id("val4")).to eq(nil) }
|
||||
end
|
||||
|
||||
context 'when the values are empty' do
|
||||
let(:value) { [] }
|
||||
it { expect(subject.next_checkbox_id("val1")).to eq(nil) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -454,7 +454,7 @@ describe DossierRebaseConcern do
|
|||
tdc_to_update.update(drop_down_list_value: "option\nupdated")
|
||||
end
|
||||
|
||||
it { expect { subject }.to change { dossier.champs_public.first.value }.from('["v1", "option"]').to('["option"]') }
|
||||
it { expect { subject }.to change { dossier.champs_public.first.value }.from('["v1","option"]').to('["option"]') }
|
||||
end
|
||||
|
||||
context 'when a dropdown unused option is removed' do
|
||||
|
|
|
@ -38,8 +38,8 @@ describe 'The user' do
|
|||
check('val1')
|
||||
check('val3')
|
||||
select('bravo', from: form_id_for('simple_choice_drop_down_list_long'))
|
||||
select_combobox('multiple_choice_drop_down_list_long', 'alp', 'alpha')
|
||||
select_combobox('multiple_choice_drop_down_list_long', 'cha', 'charly')
|
||||
select('alpha', from: form_id_for('multiple_choice_drop_down_list_long'))
|
||||
select('charly', from: form_id_for('multiple_choice_drop_down_list_long'))
|
||||
|
||||
select('Australie', from: form_id_for('pays'))
|
||||
select('Martinique', from: form_id_for('regions'))
|
||||
|
@ -97,7 +97,10 @@ describe 'The user' do
|
|||
expect(page).to have_selected_value('pays', selected: 'Australie')
|
||||
expect(page).to have_selected_value('regions', selected: 'Martinique')
|
||||
expect(page).to have_selected_value('departements', selected: '02 – Aisne')
|
||||
check_selected_value('multiple_choice_drop_down_list_long', with: ['alpha', 'charly'])
|
||||
within("##{champ_for('multiple_choice_drop_down_list_long').input_group_id}") do
|
||||
expect(page).to have_button('alpha')
|
||||
expect(page).to have_button('charly')
|
||||
end
|
||||
expect(page).to have_selected_value('communes', selected: 'Brétigny (60400)')
|
||||
expect(page).to have_selected_value('pays', selected: 'Australie')
|
||||
expect(page).to have_field('dossier_link', with: '123')
|
||||
|
@ -530,8 +533,12 @@ describe 'The user' do
|
|||
end
|
||||
|
||||
def champ_value_for(libelle)
|
||||
champ_for(libelle).value
|
||||
end
|
||||
|
||||
def champ_for(libelle)
|
||||
champs = user_dossier.reload.champs_public
|
||||
champs.find { |c| c.libelle == libelle }.value
|
||||
champs.find { |c| c.libelle == libelle }
|
||||
end
|
||||
|
||||
def fill_individual
|
||||
|
|
|
@ -101,7 +101,7 @@ describe 'shared/dossiers/edit.html.haml', type: :view do
|
|||
let(:champ_value) { ['banana', 'grapefruit'].to_json }
|
||||
|
||||
it 'renders the list as a multiple-selection dropdown' do
|
||||
expect(subject).to have_selector('[data-react-component-value="ComboMultipleDropdownList"]')
|
||||
expect(subject).to have_selector('select')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue