From ba0211ba52462fcf1f836f96ec23cc261663fae8 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 26 Oct 2021 16:21:47 +0200 Subject: [PATCH] feat(champ): ask for departement before asking for commune --- .../components/ComboCommunesSearch.jsx | 76 ++++++++++++++++--- .../components/ComboDepartementsSearch.jsx | 22 ++++-- app/javascript/components/ComboSearch.jsx | 61 ++++++++++----- .../components/shared/queryClient.js | 14 ++-- config/routes.rb | 1 + spec/support/system_helpers.rb | 6 +- spec/system/users/brouillon_spec.rb | 1 + 7 files changed, 132 insertions(+), 49 deletions(-) diff --git a/app/javascript/components/ComboCommunesSearch.jsx b/app/javascript/components/ComboCommunesSearch.jsx index c1b0f80d0..b02c5070e 100644 --- a/app/javascript/components/ComboCommunesSearch.jsx +++ b/app/javascript/components/ComboCommunesSearch.jsx @@ -1,9 +1,10 @@ -import React from 'react'; +import React, { useState, useMemo } from 'react'; import { QueryClientProvider } from 'react-query'; import { matchSorter } from 'match-sorter'; import ComboSearch from './ComboSearch'; import { queryClient } from './shared/queryClient'; +import { ComboDepartementsSearch } from './ComboDepartementsSearch'; // Avoid hiding similar matches for precise queries (like "Sainte Marie") function searchResultsLimit(term) { @@ -36,20 +37,71 @@ function expandResultsWithMultiplePostalCodes(term, results) { return expandedResults; } +const placeholderDepartements = [ + ['63 – Puy-de-Dôme', 'Clermont-Ferrand'], + ['77 – Seine-et-Marne', 'Melun'], + ['22 – Côtes d’Armor', 'Saint-Brieuc'], + ['47 – Lot-et-Garonne', 'Agen'] +]; +const [placeholderDepartement, placeholderCommune] = + placeholderDepartements[ + Math.floor(Math.random() * (placeholderDepartements.length - 1)) + ]; + function ComboCommunesSearch(params) { + const [departementCode, setDepartementCode] = useState(); + const inputId = useMemo( + () => + document.querySelector(`input[data-uuid="${params.hiddenFieldId}"]`)?.id, + [params.hiddenFieldId] + ); + const departementDescribedBy = `${inputId}_departement_notice`; + const communeDescribedBy = `${inputId}_commune_notice`; + return ( - [ - code, - `${nom} (${codesPostaux[0]})` - ]} - transformResults={expandResultsWithMultiplePostalCodes} - /> +
+
+

+ Choisissez le département dans lequel se situe la commune. Vous + pouvez entrer le nom ou le code. +

+
+ { + setDepartementCode(result?.code); + }} + /> +
+ {departementCode ? ( +
+
+

+ Choisissez la commune. Vous pouver entre le nom ou le code postal. +

+
+ [ + code, + `${nom} (${codesPostaux[0]})` + ]} + transformResults={expandResultsWithMultiplePostalCodes} + /> +
+ ) : null}
); } diff --git a/app/javascript/components/ComboDepartementsSearch.jsx b/app/javascript/components/ComboDepartementsSearch.jsx index 6afd68b1c..745a29803 100644 --- a/app/javascript/components/ComboDepartementsSearch.jsx +++ b/app/javascript/components/ComboDepartementsSearch.jsx @@ -16,19 +16,27 @@ function expandResultsWithForeignDepartement(term, results) { ]; } -function ComboDepartementsSearch(params) { +export function ComboDepartementsSearch(params) { + return ( + [code, `${code} - ${nom}`]} + transformResults={expandResultsWithForeignDepartement} + /> + ); +} + +function ComboDepartementsSearchDefault(params) { return ( - [code, `${code} - ${nom}`]} - transformResults={expandResultsWithForeignDepartement} /> ); } -export default ComboDepartementsSearch; +export default ComboDepartementsSearchDefault; diff --git a/app/javascript/components/ComboSearch.jsx b/app/javascript/components/ComboSearch.jsx index 5bcf4d559..563f494d5 100644 --- a/app/javascript/components/ComboSearch.jsx +++ b/app/javascript/components/ComboSearch.jsx @@ -1,4 +1,10 @@ -import React, { useState, useMemo, useCallback, useRef } from 'react'; +import React, { + useState, + useMemo, + useCallback, + useRef, + useEffect +} from 'react'; import { useDebounce } from 'use-debounce'; import { useQuery } from 'react-query'; import PropTypes from 'prop-types'; @@ -19,22 +25,25 @@ function defaultTransformResults(_, results) { } function ComboSearch({ - placeholder, - required, hiddenFieldId, onChange, scope, + inputId, + scopeExtra, minimumInputLength, transformResult, allowInputValues = false, transformResults = defaultTransformResults, - className + ...props }) { - const label = scope; const hiddenValueField = useMemo( () => document.querySelector(`input[data-uuid="${hiddenFieldId}"]`), [hiddenFieldId] ); + const comboInputId = useMemo( + () => hiddenValueField?.id || inputId, + [inputId, hiddenValueField] + ); const hiddenIdField = useMemo( () => document.querySelector( @@ -81,15 +90,18 @@ function ComboSearch({ const handleOnChange = useCallback( ({ target: { value } }) => { setValue(value); - if (value.length >= minimumInputLength) { + if (!value) { + setExternalId(''); + setExternalValue(''); + if (onChange) { + onChange(null); + } + } else if (value.length >= minimumInputLength) { setSearchTerm(value.trim()); if (allowInputValues) { setExternalId(''); setExternalValue(value); } - } else if (!value) { - setExternalId(''); - setExternalValue(''); } }, [minimumInputLength] @@ -102,11 +114,14 @@ function ComboSearch({ awaitFormSubmit.done(); }, []); - const { isSuccess, data } = useQuery([scope, debouncedSearchTerm], { - enabled: !!debouncedSearchTerm, - notifyOnStatusChange: false, - refetchOnMount: false - }); + const { isSuccess, data } = useQuery( + [scope, debouncedSearchTerm, scopeExtra], + { + enabled: !!debouncedSearchTerm, + notifyOnStatusChange: false, + refetchOnMount: false + } + ); const results = isSuccess ? transformResults(debouncedSearchTerm, data) : []; const onBlur = useCallback(() => { @@ -118,15 +133,20 @@ function ComboSearch({ } }, [data]); + useEffect(() => { + document + .querySelector(`#${comboInputId}[type="hidden"]`) + ?.removeAttribute('id'); + }, [comboInputId]); + return ( - + {isSuccess && ( @@ -157,8 +177,6 @@ function ComboSearch({ } ComboSearch.propTypes = { - placeholder: PropTypes.string, - required: PropTypes.bool, hiddenFieldId: PropTypes.string, scope: PropTypes.string, minimumInputLength: PropTypes.number, @@ -166,7 +184,8 @@ ComboSearch.propTypes = { transformResults: PropTypes.func, allowInputValues: PropTypes.bool, onChange: PropTypes.func, - className: PropTypes.string + inputId: PropTypes.string, + scopeExtra: PropTypes.string }; export default ComboSearch; diff --git a/app/javascript/components/shared/queryClient.js b/app/javascript/components/shared/queryClient.js index 9a5471252..e37e4fb7f 100644 --- a/app/javascript/components/shared/queryClient.js +++ b/app/javascript/components/shared/queryClient.js @@ -26,17 +26,21 @@ export const queryClient = new QueryClient({ } }); -function buildURL(scope, term) { +function buildURL(scope, term, extra) { term = encodeURIComponent(term.replace(/\(|\)/g, '')); if (scope === 'adresse') { return `${api_adresse_url}/search?q=${term}&limit=${API_ADRESSE_QUERY_LIMIT}`; } else if (scope === 'annuaire-education') { return `${api_education_url}/search?dataset=fr-en-annuaire-education&q=${term}&rows=${API_EDUCATION_QUERY_LIMIT}`; } else if (scope === 'communes') { + const limit = `limit=${API_GEO_COMMUNES_QUERY_LIMIT}`; + const url = extra + ? `${api_geo_url}/communes?codeDepartement=${extra}&${limit}&` + : `${api_geo_url}/communes?${limit}&`; if (isNumeric(term)) { - return `${api_geo_url}/communes?codePostal=${term}&limit=${API_GEO_COMMUNES_QUERY_LIMIT}`; + return `${url}codePostal=${term}`; } - return `${api_geo_url}/communes?nom=${term}&boost=population&limit=${API_GEO_COMMUNES_QUERY_LIMIT}`; + return `${url}nom=${term}&boost=population`; } else if (isNumeric(term)) { const code = term.padStart(2, '0'); return `${api_geo_url}/${scope}?code=${code}&limit=${API_GEO_QUERY_LIMIT}`; @@ -53,12 +57,12 @@ function buildOptions() { return [{}, null]; } -async function defaultQueryFn({ queryKey: [scope, term] }) { +async function defaultQueryFn({ queryKey: [scope, term, extra] }) { if (scope == 'pays') { return matchSorter(await getPays(), term, { keys: ['label'] }); } - const url = buildURL(scope, term); + const url = buildURL(scope, term, extra); const [options, controller] = buildOptions(); const promise = fetch(url, options).then((response) => { if (response.ok) { diff --git a/config/routes.rb b/config/routes.rb index 38bb0241d..249faa38c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -467,6 +467,7 @@ Rails.application.routes.draw do get 'regions' => 'api_geo_test#regions' get 'communes' => 'api_geo_test#communes' get 'departements' => 'api_geo_test#departements' + get 'departements/:code/communes' => 'api_geo_test#communes' end end diff --git a/spec/support/system_helpers.rb b/spec/support/system_helpers.rb index 3cda3ddb2..0db4c350c 100644 --- a/spec/support/system_helpers.rb +++ b/spec/support/system_helpers.rb @@ -104,13 +104,11 @@ module SystemHelpers end def select_combobox(champ, fill_with, value) - input = find("input[aria-label=\"#{champ}\"") - input.click - input.fill_in with: fill_with + fill_in champ, with: fill_with selector = "li[data-option-value=\"#{value}\"]" find(selector).click expect(page).to have_css(selector) - expect(page).to have_hidden_field(champ, with: value) + expect(page).to have_css("[type=\"hidden\"][value=\"#{value}\"]") end def select_multi_combobox(champ, fill_with, value) diff --git a/spec/system/users/brouillon_spec.rb b/spec/system/users/brouillon_spec.rb index 16cf8156d..9912d827f 100644 --- a/spec/system/users/brouillon_spec.rb +++ b/spec/system/users/brouillon_spec.rb @@ -32,6 +32,7 @@ describe 'The user' do select_combobox('pays', 'aust', 'Australie') select_combobox('regions', 'Ma', 'Martinique') select_combobox('departements', 'Ai', '02 - Aisne') + select_combobox('communes', 'Ai', '02 - Aisne') select_combobox('communes', 'Ambl', 'Ambléon (01300)') check('engagement')