fix(combobox): can copy past multiple values in restricted multi select combobox

This commit is contained in:
Paul Chavard 2024-09-23 15:20:59 +02:00
parent e35584811c
commit fb07a0ca54
No known key found for this signature in database
5 changed files with 43 additions and 24 deletions

View file

@ -17,6 +17,7 @@ class EditableChamp::MultipleDropDownListComponent < EditableChamp::EditableCham
name: @form.field_name(:value, multiple: true), name: @form.field_name(:value, multiple: true),
selected_keys: @champ.selected_options, selected_keys: @champ.selected_options,
items: @champ.enabled_non_empty_options, items: @champ.enabled_non_empty_options,
value_separator: false,
'aria-label': @champ.libelle, 'aria-label': @champ.libelle,
'aria-describedby': @champ.describedby_id, 'aria-describedby': @champ.describedby_id,
'aria-labelledby': @champ.labelledby_id) 'aria-labelledby': @champ.labelledby_id)

View file

@ -1,5 +1,5 @@
= form_tag update_displayed_fields_instructeur_procedure_path(@procedure), method: :patch, class: 'dropdown-form large columns-form' do = form_tag update_displayed_fields_instructeur_procedure_path(@procedure), method: :patch, class: 'dropdown-form large columns-form' do
%react-fragment %react-fragment
= render ReactComponent.new "ComboBox/MultiComboBox", items: @displayable_columns_for_select, selected_keys: @displayable_columns_selected, name: 'values[]', 'aria-label': 'Colonne à afficher' = render ReactComponent.new "ComboBox/MultiComboBox", items: @displayable_columns_for_select, selected_keys: @displayable_columns_selected, name: 'values[]', 'aria-label': 'Colonne à afficher', value_separator: false
= submit_tag t('.save'), class: 'fr-btn fr-btn--secondary' = submit_tag t('.save'), class: 'fr-btn fr-btn--secondary'

View file

@ -168,13 +168,18 @@ export function useMultiList({
defaultItems?: Item[]; defaultItems?: Item[];
defaultSelectedKeys?: string[]; defaultSelectedKeys?: string[];
allowsCustomValue?: boolean; allowsCustomValue?: boolean;
valueSeparator?: string; valueSeparator?: string | false;
onChange?: () => void; onChange?: () => void;
focusInput?: () => void; focusInput?: () => void;
formValue?: 'text' | 'key'; formValue?: 'text' | 'key';
}) { }) {
const valueSeparatorRegExp = useMemo( const valueSeparatorRegExp = useMemo(
() => (valueSeparator ? new RegExp(valueSeparator) : /\s|,|;/), () =>
valueSeparator === false
? false
: valueSeparator
? new RegExp(valueSeparator)
: /\s|,|;/,
[valueSeparator] [valueSeparator]
); );
const [selectedKeys, setSelectedKeys] = useState( const [selectedKeys, setSelectedKeys] = useState(
@ -219,7 +224,7 @@ export function useMultiList({
const values = selectedItems.map((item) => const values = selectedItems.map((item) =>
formValue == 'text' || allowsCustomValue ? item.label : item.value formValue == 'text' || allowsCustomValue ? item.label : item.value
); );
if (!allowsCustomValue || inputValue == '') { if (!valueSeparatorRegExp || !allowsCustomValue || inputValue == '') {
return values; return values;
} }
return [ return [
@ -269,11 +274,25 @@ export function useMultiList({
setInputValue(''); setInputValue('');
return; return;
} }
if (allowsCustomValue) {
if (!valueSeparatorRegExp) {
setInputValue(value);
return;
}
const values = value.split(valueSeparatorRegExp); const values = value.split(valueSeparatorRegExp);
if (values.length < 2) {
setInputValue(value);
return;
}
// if input contains a separator, add all values // if input contains a separator, add all values
if (values.length > 1) { const addedKeys = allowsCustomValue
const addedKeys = values.filter(Boolean); ? values.filter(Boolean)
: values
.filter(Boolean)
.map((value) => items.find((item) => item.label == value)?.value)
.filter((key) => key != null);
setSelectedKeys((keys) => { setSelectedKeys((keys) => {
const selectedKeys = new Set(keys.values()); const selectedKeys = new Set(keys.values());
for (const key of addedKeys) { for (const key of addedKeys) {
@ -281,14 +300,8 @@ export function useMultiList({
} }
return selectedKeys; return selectedKeys;
}); });
setInputValue('');
} else {
setInputValue(value);
}
onChange?.(); onChange?.();
} else { setInputValue('');
setInputValue(value);
}
} }
); );

View file

@ -56,7 +56,7 @@ export const MultiComboBoxProps = s.assign(
s.object({ s.object({
selectedKeys: s.array(s.string()), selectedKeys: s.array(s.string()),
allowsCustomValue: s.boolean(), allowsCustomValue: s.boolean(),
valueSeparator: s.string() valueSeparator: s.union([s.string(), s.literal(false)])
}) })
) )
); );

View file

@ -7,6 +7,8 @@ describe 'Inviting an expert:', js: true do
let(:instructeur) { create(:instructeur, password: SECURE_PASSWORD) } let(:instructeur) { create(:instructeur, password: SECURE_PASSWORD) }
let(:expert) { create(:expert, password: expert_password) } let(:expert) { create(:expert, password: expert_password) }
let(:expert2) { create(:expert, password: expert_password) } let(:expert2) { create(:expert, password: expert_password) }
let(:expert3) { create(:expert, password: expert_password) }
let(:expert4) { create(:expert, password: expert_password) }
let(:expert_password) { 'mot de passe dexpert' } let(:expert_password) { 'mot de passe dexpert' }
let(:procedure) { create(:procedure, :published, instructeurs: [instructeur], types_de_champ_public: [{ type: :dossier_link }]) } let(:procedure) { create(:procedure, :published, instructeurs: [instructeur], types_de_champ_public: [{ type: :dossier_link }]) }
let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:) } let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:) }
@ -31,6 +33,7 @@ describe 'Inviting an expert:', js: true do
within('.fr-sidemenu') { click_on 'Demander un avis' } within('.fr-sidemenu') { click_on 'Demander un avis' }
expect(page).to have_current_path(avis_new_instructeur_dossier_path(procedure, dossier)) expect(page).to have_current_path(avis_new_instructeur_dossier_path(procedure, dossier))
fill_in 'Emails', with: "#{expert3.email},#{expert4.email}"
fill_in 'Emails', with: "#{expert.email}," fill_in 'Emails', with: "#{expert.email},"
fill_in 'Emails', with: expert2.email fill_in 'Emails', with: expert2.email
fill_in 'avis_introduction', with: 'Bonjour, merci de me donner votre avis sur ce dossier.' fill_in 'avis_introduction', with: 'Bonjour, merci de me donner votre avis sur ce dossier.'
@ -45,10 +48,12 @@ describe 'Inviting an expert:', js: true do
within('section') do within('section') do
expect(page).to have_content(expert.email.to_s) expect(page).to have_content(expert.email.to_s)
expect(page).to have_content(expert2.email.to_s) expect(page).to have_content(expert2.email.to_s)
expect(page).to have_content(expert3.email.to_s)
expect(page).to have_content(expert4.email.to_s)
expect(page).to have_content('Bonjour, merci de me donner votre avis sur ce dossier.') expect(page).to have_content('Bonjour, merci de me donner votre avis sur ce dossier.')
end end
expect(Avis.count).to eq(4) expect(Avis.count).to eq(8)
expect(emails_sent_to(expert.email.to_s).size).to eq(1) expect(emails_sent_to(expert.email.to_s).size).to eq(1)
expect(emails_sent_to(expert2.email.to_s).size).to eq(1) expect(emails_sent_to(expert2.email.to_s).size).to eq(1)
invitation_email = open_email(expert.email.to_s) invitation_email = open_email(expert.email.to_s)