MPDX-9943 - Fix geographic multiplier clearing to save on blur - #1987
MPDX-9943 - Fix geographic multiplier clearing to save on blur#1987wjames111 wants to merge 11 commits into
Conversation
Emptying the autocomplete input fires onChange(null, 'clear') while the user is still typing, which saved geographicLocation: null immediately and reset the visible field to "None" mid-edit. Defer the null save to blur so clearing the field only mutates once the user clicks out. MPDX-9943 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
"If not applicable, select \"None\"" instructed users to do what the field already does by default. Use the generic Goal Calculator's explanatory helper text instead. MPDX-9943 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MUI's Autocomplete fires onChange(null, 'clear') the moment the typed input becomes empty, so backspacing the Geographic Location field to empty saved geographicLocation: null mid-edit and reset the field. Skip saves when the change reason is 'clear' and save null from the TextField's blur instead, matching the MPDX-9943 fix in the PDS goal calculator. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bundle sizes [mpdx-react]Compared against 8b6c94e No significant changes found |
- Guarantee a leading 'None' (0 multiplier) option in goalGeographicConstantMap so every geographic dropdown offers it even when a curated year dataset omits the row (as the 2026 data does) - Display 'None' when no location is saved in the MPD Goal Calculator, NS Goal Calculator, Salary Calculator, and NSO questionnaire, matching the existing PDS behavior - Make the NSO geographic question optional: drop its required validation and remove it from Ministry step completion, since an unanswered question now displays and calculates as 'None' - Give the Salary Calculator's AutosaveAutocomplete the same defer-clear-to-blur autosave treatment as the PDS and MPD fields, since the None default would otherwise expose the mid-typing reset MPDX-9943 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
wjames111
left a comment
There was a problem hiding this comment.
AI Review (senior) — summary
No must-fix issues. Clear/blur autosave semantics, None display defaults, and the guaranteed None option are consistent across all five tools; None/null equivalence verified in every constants-map consumer and server-side (multiplier_for! yields ×1.0 for both). Findings below are quality-level and posted as individual comments:
- [Reuse] Defer-clear-to-blur block now triplicated — extract a shared component
- [Nice-to-have] AutosaveAutocomplete's blur-null behavior is generic — document or gate
- [Nice-to-have] Transient MUI dev warnings while constants load (MPD/NS)
- [Nice-to-have] NSO's placeholder row is inert once emptyValue is set
- [Nice-to-have] NS persists 'None' while other tools persist null — normalize eventually
- [Pattern] Hook-level None seeding is correctly placed — no change requested
- [Suggested tests] Guard the yup .required() removal directly
|
[Reuse] This clear-guard + blur-save block is now copied in three places (here, MPD InformationCategoryPersonalForm, Salary AutosaveAutocomplete). Consider extracting a shared DeferredClearAutocomplete that takes saveField. (at |
|
[Nice-to-have] Empty-input-on-blur now saves null for ANY fieldName using this component. Fine for location; could 500 for a field whose null is invalid server-side. Consider a JSDoc note or an opt-in prop. (at |
|
[Nice-to-have] While constants load, value 'None' has no matching option and MUI logs a dev warning. Cosmetic only; could gate on the constants loading flag like NSO does. (at |
|
[Nice-to-have] With emptyValue set, the disabled placeholder MenuItem still renders as an inert row in the dropdown. Consider hiding the placeholder when emptyValue is provided. (at |
|
[Nice-to-have] NS now persists the string 'None' on save while PDS/MPD/Salary persist null unless None is explicitly picked. Server treats both as x1.0, but consider normalizing to one representation eventually. (at |
|
[Pattern] Seeding 'None' here is the right single point (all five dropdowns build from this map), and a server 'None' row still wins on value. No change requested. (at |
|
[Suggested tests] Consider one test that blurring the optional city question with no answer produces no validation error, guarding the yup .required() removal directly. (at |
The Salary Calculator and NSO questionnaire helper texts still instructed users to "select None", which reads as a requirement. The field is optional in every tool and defaults to "None" on its own, so tell users they can leave it as-is instead. MPDX-9943 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add JSDoc noting that clearing the input and blurring saves null, so the field's null must be valid server-side Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Gate the Geographic Location Autocomplete value and disabled state on constantsLoading so the 'None' fallback never renders against empty options and MUI's value-not-in-options dev warning cannot fire Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hide the disabled placeholder MenuItem when emptyValue is provided, since the select always displays a real option in that case Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Normalize a geographicLocation of 'None' to null on submit so NS goals persist null (the canonical no-multiplier form) instead of the string 'None' Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add test that blurring the optional city question empty shows no validation error Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extract shared DeferredClearAutocomplete for the clear-guard + blur-save autocomplete pattern and rewire all three call sites Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
🤖 Fixed in ae2ab22: [Reuse] Extracted shared HrTools/Shared/DeferredClearAutocomplete (clear-reason skip + empty-input blur-save) and rewired PDS SetupStep, MPD InformationCategoryPersonalForm, and Salary AutosaveAutocomplete to use it, with a focused test file. Posted by Claude Code via /agent-fix. |
|
🤖 Fixed in 94d70c7: [Nice-to-have] Added JSDoc on AutosaveAutocomplete warning that clearing + blurring saves null for fieldName, so it should only wrap fields whose null is valid server-side. Posted by Claude Code via /agent-fix. |
|
🤖 Fixed in acb6957: [Nice-to-have] Gated the MPD Geographic Location value on the constants loading flag so the 'None' fallback never renders against empty options (no more MUI dev warning). Posted by Claude Code via /agent-fix. |
|
🤖 Fixed in 647f4ee: [Nice-to-have] SelectQuestion now hides the disabled placeholder MenuItem when emptyValue is set, with a test. Posted by Claude Code via /agent-fix. |
|
🤖 Fixed in 7b353e6: [Nice-to-have] NS formValuesToAttributes now normalizes 'None' to null on submit so NS goals persist null as the canonical no-multiplier form, with a test. Posted by Claude Code via /agent-fix. |
|
🤖 Fixed in 7efeed0: [Suggested tests] Added a MinistryDetails test that blurring the optional city question with no answer shows no validation error, guarding the .required() removal. Posted by Claude Code via /agent-fix. |
|
Preview branch generated at https://MPDX-9943-pds-geographic-multiplier.d3dytjb8adxkk5.amplifyapp.com |
|
Playing devil's advocate, if we make None an option in the list, should we remove the clearable button? If that's an option, this seems to achieve the same thing on PDS calculator with a lot less code. I might be missing something of why your solution is better. diff --git a/src/components/HrTools/PdsGoalCalculator/Setup/SetupStep.tsx b/src/components/HrTools/PdsGoalCalculator/Setup/SetupStep.tsx
index 0e690a201..f5fba2d73 100644
--- a/src/components/HrTools/PdsGoalCalculator/Setup/SetupStep.tsx
+++ b/src/components/HrTools/PdsGoalCalculator/Setup/SetupStep.tsx
@@ -281,9 +281,10 @@ export const SetupStep: React.FC = () => {
<Grid size={12}>
<Autocomplete
options={locations}
+ disableClearable
getOptionLabel={getLocationLabel}
value={calculation?.geographicLocation ?? 'None'}
- onChange={(_, newValue: string | null) =>
+ onChange={(_, newValue) =>
saveField({ geographicLocation: newValue })
}
disabled={!calculation}
diff --git a/src/hooks/useGoalCalculatorConstants.ts b/src/hooks/useGoalCalculatorConstants.ts
index f70c67477..995f75de6 100644
--- a/src/hooks/useGoalCalculatorConstants.ts
+++ b/src/hooks/useGoalCalculatorConstants.ts
@@ -48,6 +48,7 @@ export const formatConstants = (
});
const goalGeographicConstantMap: GoalGeographicConstantMap = new Map();
+ goalGeographicConstantMap.set('None', 0);
constant?.mpdGoalGeographicConstants.forEach((constant) => {
const { location, percentageMultiplier } = constant;
goalGeographicConstantMap.set(location, percentageMultiplier);
|
canac
left a comment
There was a problem hiding this comment.
This looks really good! I'm wondering if we could get away with a simpler approach by disabling the clear button. Did you consider that?
| import React from 'react'; | ||
| import { Autocomplete, TextField, TextFieldProps } from '@mui/material'; | ||
|
|
||
| export interface DeferredClearAutocompleteProps { |
There was a problem hiding this comment.
Can we make this interface extend AutocompleteProps so that we can pass in any Autocomplete prop without having to list them here?
| AutocompleteProps<string, false, false, false>, | ||
| 'renderInput' | 'onChange' | 'value' | ||
| > { | ||
| export interface AutosaveAutocompleteProps { |
There was a problem hiding this comment.
Extending AutocompleteProps<string, false, false, false> or DeferredClearAutocomplete makes this component more flexible in the future.
| ), | ||
| ), | ||
| // Optional: an unanswered question displays and calculates as "None" | ||
| geographicLocation: yup.string().nullable(), |
There was a problem hiding this comment.
So we want the choice to default to None? I thought I remembered @zweatshirt advocating for forcing the user to make an explicit choice. I'm not opposed to this change, I just want to make sure we have consensus.
Description
Fixes the PDS Goal Calculator's Geographic Multiplier autocomplete firing an extra
UpdatePdsGoalCalculationmutation when the field is backspaced to empty: MUI's Autocomplete firesonChange(null, 'clear')the moment the typed input becomes empty, which savedgeographicLocation: nullmid-edit and reset the visible field to "None" while the user was still typing. Saves with the'clear'reason are now deferred, andnullis saved from the field's blur instead — so clearing only mutates once the user clicks out.Applies the same fix to the MPD Goal Calculator's Geographic Location autocomplete (
InformationCategoryPersonalForm), which had the identical latent bug.Replaces the PDS helper text "If not applicable, select "None"" with the MPD calculator's explanatory copy ("Do you live within 50 miles of one of these major cities?") — the old text instructed users to do something the field already does by default, since an unset value displays as "None".
Makes the geographic location optional with a "None" default across all five tools that render the constants dropdown:
None(0 multiplier) option ingoalGeographicConstantMap, so every dropdown offers it even when a curated year dataset omits the row (the 2026 data currently does — see the API PR below).AutosaveAutocompletegets the same defer-clear-to-blur autosave treatment, since the "None" default would otherwise expose the same mid-typing reset bug there.Related Jira ticket: MPDX-9943
API counterpart (restores the missing "None" constant row): https://github.com/CruGlobal/mpdx_api/pull/3547
Testing
hrTools/pdsGoalCalculator, open a goal, and go to the Setup stepgeographicLocation: nulland the field then shows "None"Checklist:
/quality:agent-reviewcommand locally and fixed any relevant suggestions🤖 Generated with Claude Code