Skip to content

Commit

Permalink
Bloquer la sélection de détections déjà sélectionnées
Browse files Browse the repository at this point in the history
Dans l'interface d'édition de la zone bloquer dans l'interface les
détections qui auraient déjà été sélectionnées dans un autre picker.
Ainsi un utilisateur ne peut plus avoir l'erreur back disant qu'une
détection donnée a été sélectionnée à la fois dans une ZI et en hors
zone infestée, ou alors dans deux ZI différentes.

J'ai essayé plusieurs approches, les contraintes a prendre en compte
étant:
- Que la recherche fonctionne toujours
- Gérer l'ajout et la supression de détection dans un picker
- Le nombre de picker est variable dans le temps (il est possible
  d'ajouter ou de supprimer des ZI)

Une autre approche était d'ajouter l'attribut disabled directement au
niveau de l'élément HTML, mais cela me forçait a demander a choiceJS le
rafraichissement des dropdowns. En utilisant setChoices, cette partie
est directement gérée par la bibliothéque.
  • Loading branch information
Anto59290 committed Feb 28, 2025
1 parent 1e03de0 commit 00b5e84
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 3 deletions.
11 changes: 11 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,14 @@ def _choice_js_cant_pick(page, locator, fill_content, exact_name):
expect(page.get_by_role("option", name=exact_name, exact=True)).not_to_be_visible()

return _choice_js_cant_pick


@pytest.fixture
def choice_js_option_disabled(db, page):
def _choice_js_cant_pick(page, locator, exact_name):
page.query_selector(locator).click()
page.wait_for_selector("input:focus", state="visible", timeout=2_000)
element = page.locator(locator).locator("xpath=..").locator(".choices__item--choice")
expect(element.get_by_role("option", name=exact_name, exact=True))

return _choice_js_cant_pick
68 changes: 65 additions & 3 deletions sv/static/sv/fichezone_form.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
let pickedDetections = []
let allChoices = []

function rebuildDetectionOptions(detectionChoices){
detectionChoices.enable()
const currentChoices = detectionChoices.passedElement.optionsAsChoices()
const updatedChoices = currentChoices.map(choice => ({
value: choice.value,
label: choice.label,
disabled: pickedDetections.includes(choice.value),
selected: choice.selected
}));
detectionChoices.clearChoices()
detectionChoices.setChoices(updatedChoices, 'value', 'label', false);
}


function rebuildChoicesOptions(){
allChoices.forEach(choices => { rebuildDetectionOptions(choices)})
}

function initializeChoices(elementId) {
options = {
searchResultLimit: 500,
Expand All @@ -9,8 +30,14 @@ function initializeChoices(elementId) {
noResultsText: 'Aucun résultat trouvé',
noChoicesText: 'Aucune fiche détection à sélectionner',
searchFields: ['label'],
};
new Choices(document.getElementById(elementId), options);
}
let choices = new Choices(document.getElementById(elementId), options)
choices.passedElement.element.addEventListener('change', event=> {
pickedDetections.push(event.detail.value)
rebuildChoicesOptions()
})
allChoices.push(choices)
rebuildDetectionOptions(choices)
}

function initializeAllChoices() {
Expand All @@ -20,6 +47,14 @@ function initializeAllChoices() {
}
}

function initializepickedDetections() {
allChoices.forEach((detectionChoice) =>{
detectionChoice.getValue().forEach((item) =>{
pickedDetections.push(item.value)
})
} )
}


function getNextIdToUse(){
let num = 0
Expand All @@ -36,7 +71,7 @@ function addZoneInfesteeForm() {
document.getElementById('zones-infestees').insertAdjacentHTML('beforeend', newTabTemplate);
const newDeleteBtn = document.querySelector(`#modal-delete-zi-confirmation-${nextIdToUse} .delete-zone-infestee`)
newDeleteBtn.addEventListener("click", removeZoneInfesteeForm)
new Choices(document.getElementById(`id_zoneinfestee_set-${nextIdToUse}-detections`), options);
initializeChoices(`id_zoneinfestee_set-${nextIdToUse}-detections`)
updatetotalFormsInput()
}

Expand All @@ -57,17 +92,44 @@ function removeZoneInfesteeForm(event){

dsfr(event.target.closest("dialog")).modal.conceal()
updatetotalFormsInput()

allChoices.forEach((choices) =>{
if (zoneElement.contains(choices.passedElement.element)){
allChoices = allChoices.filter(item => item !== choices)
const selectedValues = choices.getValue(true)
pickedDetections = pickedDetections.filter(item => !selectedValues.includes(item))
}
})
rebuildChoicesOptions()
}

function enableAllOptions(){
// Some options are disabled so that the user can't pick the detection twice in different pickers
// But we need to re-enabled the options because disabled choices are not sent in the form
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-the-form-data-set
allChoices.forEach(detectionChoices =>{
Array.from(detectionChoices.passedElement.element.options).forEach(option => {
option.disabled = false
})
})
}

document.addEventListener('DOMContentLoaded', function() {

initializeChoices('id_detections_hors_zone');
initializeAllChoices();
initializepickedDetections();
rebuildChoicesOptions();
const addZoneButton = document.getElementById('add-zone-infestee');
addZoneButton.addEventListener('click', function(event) {
event.preventDefault();
addZoneInfesteeForm();
});
document.querySelectorAll("button.delete-zone-infestee").forEach(element => element.addEventListener("click", removeZoneInfesteeForm))

document.getElementById("save-btn").addEventListener("click", event =>{
event.preventDefault()
enableAllOptions()
document.getElementById("fiche-zone-delimitee-form").submit()
})
});
2 changes: 2 additions & 0 deletions sv/tests/test_fichezonedelimitee_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ def test_cant_have_same_detection_in_hors_zone_infestee_and_zone_infestee(live_s
form_page = FicheZoneDelimiteeFormPage(page, choice_js_fill)

form_page.goto_create_form_page(live_server, evenement)
page.evaluate("window.rebuildDetectionOptions = function() {};") # Bypass front-end protection
form_page.fill_form(fiche_zone_delimitee, zone_infestee, (fiche_detection,), (fiche_detection,))
form_page.save()

Expand All @@ -209,6 +210,7 @@ def test_cant_have_same_detection_in_zone_infestee_forms(live_server, page: Page
form_page = FicheZoneDelimiteeFormPage(page, choice_js_fill)

form_page.goto_create_form_page(live_server, evenement)
page.evaluate("window.rebuildDetectionOptions = function() {};") # Bypass front-end protection
form_page.fill_form(fiche_zone_delimitee, zone_infestee1, (), (fiche_detection,))
form_page.add_new_zone_infestee(zone_infestee2, (fiche_detection,))
form_page.save()
Expand Down
69 changes: 69 additions & 0 deletions sv/tests/test_fichezonedelimitee_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ def test_update_form_cant_have_same_detection_in_hors_zone_infestee_and_zone_inf

form_page = FicheZoneDelimiteeFormPage(page, choice_js_fill)
page.goto(f"{live_server.url}{fiche_zone_delimitee.get_update_url()}")
page.wait_for_function("pickedDetections !== undefined")
page.evaluate("pickedDetections.push = function() {};")
page.evaluate("pickedDetections = [];") # Bypass front-end protection
page.evaluate("window.rebuildChoicesOptions();")
form_page.select_detections_in_zone_infestee(0, (fiche_detection,))
form_page.save()
expect(
Expand All @@ -159,6 +163,7 @@ def test_update_form_cant_have_same_detection_in_two_zone_infestee(live_server,
fiche_detection.save()
form_page = FicheZoneDelimiteeFormPage(page, choice_js_fill)
page.goto(f"{live_server.url}{fiche_zone_delimitee.get_update_url()}")
page.evaluate("window.rebuildDetectionOptions = function() {};") # Bypass front-end protection
form_page.add_new_zone_infestee(ZoneInfesteeFactory(), (fiche_detection,))
form_page.save()
expect(
Expand Down Expand Up @@ -339,3 +344,67 @@ def test_shows_correct_organisme_and_statut(live_server, page: Page):

expect(page.get_by_text(str(evenement.organisme_nuisible))).to_be_visible()
expect(page.get_by_text(str(evenement.statut_reglementaire))).to_be_visible()


def test_update_form_cant_have_same_detection_in_hors_zone_infestee_and_zone_infestee_check_ui(
live_server,
page: Page,
choice_js_fill,
choice_js_option_disabled,
):
fiche_detection = FicheDetectionFactory()
fiche_zone_delimitee = FicheZoneFactory()
evenement = fiche_detection.evenement
evenement.fiche_zone_delimitee = fiche_zone_delimitee
evenement.save()
ZoneInfesteeFactory(fiche_zone_delimitee=fiche_zone_delimitee)
fiche_detection.hors_zone_infestee = fiche_zone_delimitee
fiche_detection.save()

FicheZoneDelimiteeFormPage(page, choice_js_fill)
page.goto(f"{live_server.url}{fiche_zone_delimitee.get_update_url()}")
choice_js_option_disabled(
page,
"#zones-infestees .fr-col-4:nth-of-type(1) .choices__input--cloned:first-of-type",
str(fiche_detection.numero),
)


def test_update_form_cant_have_same_detection_in_hors_zone_infestee_and_in_new_zone_infestee_check_ui(
live_server,
page: Page,
choice_js_fill,
choice_js_option_disabled,
):
fiche_detection = FicheDetectionFactory()
fiche_zone_delimitee = FicheZoneFactory()
evenement = fiche_detection.evenement
evenement.fiche_zone_delimitee = fiche_zone_delimitee
evenement.save()
ZoneInfesteeFactory(fiche_zone_delimitee=fiche_zone_delimitee)
fiche_detection.hors_zone_infestee = fiche_zone_delimitee
fiche_detection.save()
other_detection = FicheDetectionFactory(evenement=evenement)

FicheZoneDelimiteeFormPage(page, choice_js_fill)
page.goto(f"{live_server.url}{fiche_zone_delimitee.get_update_url()}")

form_page = FicheZoneDelimiteeFormPage(page, choice_js_fill)
form_page.add_new_zone_infestee(ZoneInfesteeFactory.build(), ())
choice_js_option_disabled(
page,
"#zones-infestees .fr-col-4:nth-of-type(2) .choices__input--cloned:first-of-type",
str(fiche_detection.numero),
)

choice_js_fill(
page,
"#zones-infestees .fr-col-4:nth-of-type(2) .choices__input--cloned:first-of-type",
str(other_detection.numero),
str(other_detection.numero),
)
choice_js_option_disabled(
page,
".fichezoneform__detections-hors-zone-infestee .choices__list--multiple",
str(other_detection.numero),
)

0 comments on commit 00b5e84

Please sign in to comment.