-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(mrf): frontend field locking #7091
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM some nits / questions
...dmin-form/create/workflow/components/WorkflowContent/InactiveStepBlock/InactiveStepBlock.tsx
Show resolved
Hide resolved
...dmin-form/create/workflow/components/WorkflowContent/InactiveStepBlock/InactiveStepBlock.tsx
Outdated
Show resolved
Hide resolved
frontend/src/features/form/utils/augmentWithWorkflowDisabling.ts
Outdated
Show resolved
Hide resolved
() => | ||
formFields | ||
.map(augmentWithMyInfo) | ||
.map(augmentWithWorkflowDisabling.bind(this, workflowStep)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noob question: why there's a need to bind here? Didn't see any usage of this
in the inner function calls of augmentWithWorkflowDisabling
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated in 17083cf
Actually here i was a bit annoyed at how i have to give a variable name haha. Basically the idea is I want a partially-applied function, with only the first argument provided. There are three ways of doing it
- Write the function with two parameters, and create an anonymous function inline (this is the way i did it in the updated commit, above)
// Define
const augmentWithWorkflowDisabling = (workflowStep, formFields) => ...
// Call
formFields.map((fields) => augmentWithWorkflowDisabling(workflowStep, fields))
The advantage is this is all very nice and maintainable, we're used to functions with multiple parameters, but is annoying because i shouldn't really need to define a new variable and think about it's name and what it means just to pass it in to a function and never do anything else interesting with it.
- Write the function with two parameters, and bind it to create a partially applied function (this is the way I did it previously)
// Define
const augmentWithWorkflowDisabling = (workflowStep, formFields) => ...
// Call
formFields.map(augmentWithWorkflowDisabling.bind(this, workflowStep))
In this case, this is nice because I've removed the need to define a new variable, but now i include this random reference to ".bind" which no one really understands, and references a context "this" which doesn't exist (it's the global module context, which is a bit weird). Also not ideal
- Curry the function, and partially apply it as usual (my preferred way, but not typical coding pattern in FormSG)
// Define
const augmentWithWorkflowDisabling = (workflowStep) => (formFields) => ...
// Call
formFields.map(augmentWithWorkflowDisabling(workflowStep))
This is very nice when used in this scenario, solves all the problems and is easy to read. But, using curried functions aren't very typical in the codebase, and will look weird if used in other places where partial application is not necessary (e.g. if someone else down the line needs to use augmentWithWorkflowDisabling
but has both workflowStep
and formFields
available in the surrounding context, the call signature becomes augmentWithWorkflowDisabling(workflowStep)(formFields)
, which can look confusing in a sea of typically-uncurried functions).
In the end I think just going with 1 makes the most sense and is most consistent with the rest of the codebase, even though i like 3 the most hahaha
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://ramdajs.com/docs/#curry helps for 3. But ramda isn't a library that FormSG uses.
I notice that lodash also supports placeholders in curried functions. But this can be an exercise for the next time.
I've seen 1/3, but never 2 😆.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If i have to write .curry
then that feels a bit like, it defeats the purpose of currying anyway though. HAHAHA
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: ramda, What?? what is this javascript magic that allows you to write f(1)(2,3)
or f(1,2)(3)
tf 🤣
Problem
We want to implement field locking for MRF. This PR does this for frontend locking. Backend locking validation will be implemented in a separate PR.
Closes FRM-1639
Solution
Admin-side
EditStepBlock
), add a new component,QuestionsBlock
which contains the multiselect dropdown for admins to choose questions to edit.InactiveStepBlock
) with new section to display field labels for selected questions to be edited by each step.edit
key toshared/*
,PATCH
ed 😢.Public-side
augmentWithWorkflowDisabling
is added, which disables the field only if the field is in the workflow step, and it's an input field (i.e. not header, paragraph or image). This uses thedisabled
key in theFormFieldDto
type.disabled
key. In particular, phone numbers and the table field were updated.disableRequiredValidation
key in theFieldFactory
component. However this time instead of checking if the form is MRF and disabling the validation universally, we implement a field-level check using the same util asaugmentWithWorkflowDisabling
so that the logic is always consistent.Breaking Changes
Before & After Screenshots
Tests
Deploy Notes
New scripts:
20240214_mrf-workflow-field-locking
: This DB migration script adds theedit
key for all workflow steps, which allows users to choose the fields each respondent is allowed to edit. To preserve existing behavior, all respondents must be allowed to edit all fields. Therefore, a copy of the form fields (excl non-input fieldssection
,statement
andimage
) are copied into everyedit
key.