SAA-645 Require note on savings fund transfer form#1772
Conversation
|
Preview branch generated at https://SAA-645-require-note-on-transfer-form.d3dytjb8adxkk5.amplifyapp.com |
Bundle sizes [mpdx-react]Compared against 1dea58b No significant changes found |
jeff-clay
left a comment
There was a problem hiding this comment.
Small, well-scoped form/validation change. All SAA-645 acceptance criteria met (required field with visual asterisk, whitespace-only rejected via .trim().min(1), mutation never fires when invalid). Default-note feature is a documented scope addition per product owner. Refactor to useGetUserQuery over parsing staffAccount.name is the right architectural call — Apollo's cache makes the duplicate hook call free. No must-fix issues; a few nice-to-have hardening suggestions and one optional whitespace test below.
| const type = data.type || TransferTypeEnum.New; | ||
| const isNew = type === TransferTypeEnum.New; | ||
|
|
||
| const defaultNote = lastName |
There was a problem hiding this comment.
[Nice-to-have] Consider trimming lastName before interpolating, so an upstream value like ' Sleight ' doesn't produce a default with leading/trailing whitespace. const trimmed = lastName?.trim(); const defaultNote = trimmed ? t(...) : '';
dr-bizz
left a comment
There was a problem hiding this comment.
This is looking good, but you don't need to add the min onto the yup logic as required does that already.
|
@dr-bizz Changes made. I know you approved already, but not sure if you want to do a review of changes. Not sure of protocol. |
| const buildCacheWithUser = (lastName: string) => { | ||
| const cache = createCache(); | ||
| cache.writeQuery<GetUserQuery>({ | ||
| query: GetUserDocument, | ||
| data: { | ||
| user: { | ||
| __typename: 'User', | ||
| id: 'test-user-id', | ||
| firstName: '', | ||
| lastName, | ||
| avatar: '', | ||
| staffAccountId: null, | ||
| primaryDesignation: null, | ||
| userType: UserTypeEnum.UsStaff, | ||
| preferences: null, | ||
| }, | ||
| }, | ||
| }); | ||
| return cache; | ||
| }; | ||
|
|
There was a problem hiding this comment.
There is a different way to do this. Let me type it up
dr-bizz
left a comment
There was a problem hiding this comment.
Thank you for fixing that issue. We have a few more issues that have arisen.
| lastName?: string; | ||
| } | ||
|
|
||
| const buildCacheWithUser = (lastName: string) => { |
There was a problem hiding this comment.
We don't need to build the cache. We can do the same as we've done here: src/components/DonationTable/DonationTable.test.tsx
We create the mocks on the GqlMockedProvider, which will mock the GQL queries
| .min(0.01, i18n.t('Amount must be at least $0.01')), | ||
| note: yup.string().nullable(), | ||
| note: yup.string().when('schedule', { | ||
| is: ScheduleEnum.OneTime, |
There was a problem hiding this comment.
Are we only adding the required note if it is a one time payment?
There was a problem hiding this comment.
Yeah, a few reasons:
- The note field currently only renders for one time transfers.
- The recurring GraphQL mutation doesn't accept a description
- SAA's recurring transfer synthesizes its own description server-side from the fund/account name.
| createTransfer: CreateTransferMutation; | ||
| updateRecurringTransfer: UpdateRecurringTransferMutation; | ||
| }> | ||
| cache={buildCacheWithUser(lastName)} |
There was a problem hiding this comment.
We can mock the response of the GQL query here so we don't need to store it in the cache. (I don't know if this will work out the box, as I haven't tested it, you might need to edit the GetUser)
We only need to enter the values we want to mock as we are randomly generating everything else.
| cache={buildCacheWithUser(lastName)} | |
| mocks={{ | |
| GetUser { | |
| user { | |
| id | |
| firstName | |
| lastName | |
| } | |
| } | |
| }} |
There was a problem hiding this comment.
Tried your mocks suggestion. It works for the mocks setup, but with useGetUserQuery inside the modal the data resolves async, and Formik captures initialValues at mount, so the default note ends up empty in that brief window.
Added a render-guard (if (userLoading) return null) as the smallest fix. In production it never fires (cache is warmed at app boot by UserPreferenceProvider). In tests it lets the mock resolve before assertions run, and let me drop the cache builder entirely.
If you'd prefer a different shape (e.g. compute the default via useEffect + setFieldValue, or not handle the loading case in the modal at all) happy to change. Just let me know.
dr-bizz
left a comment
There was a problem hiding this comment.
Thank you for making those edits. MPDX React is a large app, which means we need to keep it consistent throughout.
I noticed some more issues and have added my suggestions for you to implement.
The loading from GraphQL gets triggered every time it loads, both during the initial load and subsequent sequential loads.
I believe we can resolve this issue with a useMemo, which updates the default value when userData changes.
| if (userLoading) { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
We don't want to do this, as it can refetch data, and then we would no longer render the component.
See the next comment
| const { data: userData, loading: userLoading } = useGetUserQuery(); | ||
|
|
||
| if (userLoading) { | ||
| return null; | ||
| } | ||
|
|
||
| const trimmedLastName = userData?.user?.lastName?.trim(); | ||
| const defaultNote = trimmedLastName | ||
| ? t('{{lastName}} Savings Fund Transfer in MPDX', { | ||
| lastName: trimmedLastName, | ||
| }) | ||
| : ''; | ||
|
|
||
| const title = | ||
| type === TransferTypeEnum.New |
There was a problem hiding this comment.
We can use useMemo to reinitialise the variable defaultNote upon userData changing.
| const { data: userData, loading: userLoading } = useGetUserQuery(); | |
| if (userLoading) { | |
| return null; | |
| } | |
| const trimmedLastName = userData?.user?.lastName?.trim(); | |
| const defaultNote = trimmedLastName | |
| ? t('{{lastName}} Savings Fund Transfer in MPDX', { | |
| lastName: trimmedLastName, | |
| }) | |
| : ''; | |
| const title = | |
| type === TransferTypeEnum.New | |
| const { data: userData } = useGetUserQuery(); | |
| const defaultNote = useMemo(() => { | |
| const trimmedLastName = userData?.user?.lastName?.trim(); | |
| return trimmedLastName | |
| ? t('{{lastName}} Savings Fund Transfer in MPDX', { | |
| lastName: trimmedLastName, | |
| }) | |
| : ''; | |
| }, [userData, t]); | |
| const title = | |
| type === TransferTypeEnum.New |
| const renderModal = async (props: ComponentsProps = {}) => { | ||
| const result = render(<Components {...props} />); | ||
| await result.findByText(/Fund Transfer/); | ||
| return result; | ||
| }; |
There was a problem hiding this comment.
With now we're using useMemo, I'm unsure if you need this.
Description
<lastName> Savings Fund Transfer in MPDXusing the current user'slastName. Editable — clearing it triggers the required-field error.Related: SAA-645
Testing
<your-last-name> Savings Fund Transfer in MPDX.Checklist:
SAA-645since this work is tracked in the SAA project's Jira/pr-reviewcommand locally and fixed any relevant suggestions