Merge pull request #10849 from tchak/fix-copy-past-in-combobox
fix(combobox): can copy past multiple values in restricted multi select combobox
This commit is contained in:
commit
09fb256042
6 changed files with 44 additions and 25 deletions
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -179,7 +179,7 @@ export function MultiComboBox(maybeProps: MultiComboBoxProps) {
|
|||
const formResetRef = useOnFormReset(onReset);
|
||||
|
||||
return (
|
||||
<div className={`fr-ds-combobox__multiple ${className}`}>
|
||||
<div className={`fr-ds-combobox__multiple ${className ? className : ''}`}>
|
||||
{selectedItems.length > 0 ? (
|
||||
<TagGroup onRemove={onRemove} aria-label={props['aria-label']}>
|
||||
<TagList items={selectedItems} className="fr-tag-list">
|
||||
|
|
|
@ -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('');
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -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)])
|
||||
})
|
||||
)
|
||||
);
|
||||
|
|
|
@ -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 d’expert' }
|
||||
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)
|
||||
|
|
Loading…
Reference in a new issue