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),
selected_keys: @champ.selected_options,
items: @champ.enabled_non_empty_options,
value_separator: false,
'aria-label': @champ.libelle,
'aria-describedby': @champ.describedby_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
%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'

View file

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

View file

@ -56,7 +56,7 @@ export const MultiComboBoxProps = s.assign(
s.object({
selectedKeys: s.array(s.string()),
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(:expert) { 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(:procedure) { create(:procedure, :published, instructeurs: [instructeur], types_de_champ_public: [{ type: :dossier_link }]) }
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' }
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: expert2.email
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
expect(page).to have_content(expert.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.')
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(expert2.email.to_s).size).to eq(1)
invitation_email = open_email(expert.email.to_s)