-
Notifications
You must be signed in to change notification settings - Fork 1
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: [#187570392] raffle-bingo-nonessential-ques-license-task #8697
base: main
Are you sure you want to change the base?
feat: [#187570392] raffle-bingo-nonessential-ques-license-task #8697
Conversation
f1758e6
to
88eaf68
Compare
@@ -150,6 +151,7 @@ export const emptyProfileData: ProfileData = { | |||
nonEssentialRadioAnswers: {}, | |||
elevatorOwningBusiness: undefined, | |||
communityAffairsAddress: undefined, | |||
raffleBingoGames: undefined, |
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.
Context: The raffle-bingo
non-essential question is industry-agnostic, so I added the raffleBingoGames
property to ProfileData
instead of IndustrySpecificData
(or its related structures), which we typically use for industry-specific questions.
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.
ok
5bb07cf
to
919e2ca
Compare
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.
Context: I moved raffle-license-step-1.md
and raffle-license-step-2.md
from content/src/roadmaps/license-tasks/
into their own directory, content/src/roadmaps/raffle-bingo-steps/
, to account for the nuance that the Raffle Bingo Licensure process involves two distinct steps.
As a result, I had to make updates to search.tsx
, cmsCollections.ts
, loadTasks.ts
, and loadTasks.test.ts
.
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.
Context: I moved raffle-license-step-1.md
and raffle-license-step-2.md
from content/src/roadmaps/license-tasks/
into their own directory, content/src/roadmaps/raffle-bingo-steps/
, to account for the nuance that the Raffle Bingo Licensure process involves two distinct steps.
As a result, I had to make updates to search.tsx
, cmsCollections.ts
, loadTasks.ts
, and loadTasks.test.ts
.
40ffc05
to
6cb2d0f
Compare
...business, | ||
profileData: { | ||
...business.profileData, | ||
raffleBingoGames: undefined, |
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.
This is fine as a formal indicator that we're adding this to profileData
object, but because there's no data actually being set this doesn't do anything.
content/src/roadmaps/raffle-bingo-steps/raffle-license-step-2.md
Outdated
Show resolved
Hide resolved
summaryDescriptionMd: "" | ||
--- | ||
|
||
This task is intentionally blank. The content here is controlled by raffle-step-license-1 and raffle-step-license-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.
[boulder] We should talk to content about this task. Should this actually live with the rest of the license tasks? Right now this will not be sync'd to Webflow, and are they anticipating that it will be? The behavior of the task in-app will not change. This is more of a question of how they want this to work with Webflow.
If that's the case, and they do want it to sync, the content being sync'd to Webflow will be:
This task is intentionally blank. The content here is controlled by raffle-step-license-1 and raffle-step-license-2.
If they have specific content that they'd like to be displayed on Webflow that should be added here. It will not show up in the Navigator. This is exclusively in regards to data being sync'd.
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.
@AnJuHyppolite I know we discussed this with content in Slack and at TLC daily scrum, but did we ever come to a decision here?
@ReginaL1 not mission critical for this piece of work, but just want to make sure it's on your radar.
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.
@jphechter @AnJuHyppolite I believe we punted to decide later what the content for Webflow will be, since it's a complex case. For now, showing the task screens on My Account is the higher priority
); | ||
}; | ||
|
||
it("renders first page without clicking stepper", async () => { |
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.
[sand] The behavior we're trying to capture here is that the first screen is displayed. I don't think referencing the stepper is immediately relevant for this test. Consider renaming this test to better capture the desired behavior.
it("renders first page without clicking stepper", async () => { | |
it("renders first tab content when the task is opened", async () => { |
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.
@AnJuHyppolite Marking this unresolved. This is the same test name.
return ( | ||
<div> | ||
<TaskHeader task={props.task} /> | ||
<RaffleBingoPaginator task={props.task} /> |
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.
I know this is a pattern we follow other places in the application, but I'm not totally sold on shoving all of the task logic into the paginator. The only thing the task component is really doing here is adding a TaskHeader
. Personally, I'd opt for either keeping all the logic in a single file (e.g. moving everything in RaffleBingoPaginator
up one level into RaffleBingoTask
), or divvying up logical responsibilities between the paginator component and the task component.
This just feels like RaffleBingoTask
doesn't have purpose.
Curious what your thoughts are here.
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.
➕
); | ||
expect(getLastCalledWith(mockRoadmapBuilder)[0].addOns).not.toContain("raffle-bingo-games"); | ||
}); | ||
}); |
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.
[boulder] We should be covering all our negative test cases here as well. Given that we are fully expecting that a business must be a nonprofit in order to get this addon, there should be a test for the case that raffleBingoGames
is true
, legalStructureId
is not nonprofit
and we assert that the addon does not get added.
it("adds raffle-bingo-games task if raffle-bingo-games business", () => { | ||
const profileData = generateStartingProfile({ | ||
raffleBingoGames: true, | ||
isNonprofitOnboardingRadio: true, |
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.
This line isn't impacting the logic in buildUserRoadmap
. Strongly advise we leave this out. This is a case where we're staging data that does not actually support the test case, which is something else we've been talking about as a team.
isNonprofitOnboardingRadio: true, |
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.
I updated the test names to account for the non-profit legal structure that is needed in order for a business to get a raffle-bingo
prompt and added a test for non-profits that do not conduct raffle/bingo games.
const displayRaffleBingoGameQuestion = (): boolean => { | ||
if (!business) return false; | ||
return profileData.legalStructureId === "nonprofit"; | ||
}; |
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.
[boulder] Should we add a test for this?
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.
Yes, added.
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.
Should we also add tests to assert that this doesn't appear on the Oscar and Dakota profiles as well?
6cb2d0f
to
96705a8
Compare
web/public/mgmt/config.yml
Outdated
@@ -2522,6 +2522,54 @@ collections: | |||
- { label: "school/course", value: "school-course" } | |||
- { label: "object or vehicle", value: "object-vehicle" } | |||
|
|||
- name: "raffle-bingo-steps" | |||
label: "Raffle Bingo Steps (Navigator with Webflow mappings)" |
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.
[sand] This should be removed
label: "Raffle Bingo Steps (Navigator with Webflow mappings)" | |
label: "Raffle Bingo Steps" |
create: true | ||
editor: | ||
preview: true | ||
fields: |
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.
[boulder] Take another look at these fields. It looks like we aren't representing the collection items correctly (Ex: body is missing, we don't need license name, etc)
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.
@AnJuHyppolite body is still missing.
- name: "raffle-bingo-steps" | ||
label: "Raffle Bingo Steps (Navigator with Webflow mappings)" | ||
folder: "content/src/roadmaps/raffle-bingo-steps" | ||
summary: "{{displayname}} - {{name}}" |
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.
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.
I had a different DX. raffle-bingo-step-1
rendered in the CMS, and I found that raffle-bingo-step-1
had a displayname
and raffle-bingo-step-2
did not. I addeddisplayname
to raffle-bingo-step-2
and now both steps render with their display names.
This approach follows the existing pattern in the CMS, where the display name of the task precedes the license task (name).
editor: | ||
preview: true |
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.
96705a8
to
470cd15
Compare
await waitFor(() => { | ||
expect(screen.getByText("Application Requirements")).toBeInTheDocument(); | ||
}); |
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.
await waitFor(() => { | |
expect(screen.getByText("Application Requirements")).toBeInTheDocument(); | |
}); | |
expect(screen.findByText("Application Requirements")).toBeInTheDocument(); |
[dust] No need to make this change but findByText
will actually do the same thing waitFor()
does in a less verbose way for this case.
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.
[sand] This file of helper functions is a bit of a new pattern in this directory. I think it would be fine to just unpack these declarations into the component files where they are used since they are each quite small but open to other thoughts.
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.
I like the way u think
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.
Whole-heartedly agree.
file = await import(`@businessnjgovnavigator/content/roadmaps/municipal-tasks/${filename}.md`); | ||
try { | ||
file = await import(`@businessnjgovnavigator/content/roadmaps/municipal-tasks/${filename}.md`); | ||
} catch { | ||
file = await import(`@businessnjgovnavigator/content/roadmaps/raffle-bingo-steps/${filename}.md`); | ||
} |
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.
[dust] This might actually be a case where Promise
chaining via .catch()
would be preferable to prevent nesting.
export const shouldDisplayContinueButton = (stepIndex: number): boolean => { | ||
return stepIndex !== RaffleBingoSteps.length - 1; | ||
}; | ||
|
||
export const shouldDisplayBackButton = (stepIndex: number): boolean => { | ||
return stepIndex !== 0; | ||
}; | ||
|
||
export const shouldDisplayMarkAsCompleteButton = (stepIndex: number): boolean => { | ||
return stepIndex === RaffleBingoSteps.length - 1; | ||
}; |
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.
[dust] Tiny thing here but I find it a better practice to name things based on what values they represent instead of where they are used as it makes them more flexible and increases readability. One way you can directly see the benefit of this is that we could replace the two shouldDisplayContinueButton
and shouldDisplayMarkAsCompleteButton
functions with just one isLastStep
function. In the other file where that function is used, you can tell exactly what that new name represents without having to jump into this file. I would go with isNotFirstStep
or something similar for the other one.
@@ -1,17 +1,12 @@ | |||
--- | |||
notesMd: "[2526: Add non-essential question tasks: raffle task screen](https://dev.azure.com/NJInnovation/Business%20First%20Stop/_workitems/edit/2526)" | |||
id: "raffle-license-step-1" | |||
webflowId: "66be3c5382fc36dffc2ce314" |
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.
When you merge this, remind me so that we can remove these from Webflow. I think the sync job will take care of this, but just incase we can proactively delete them.
470cd15
to
7c8b756
Compare
7c8b756
to
a6843a6
Compare
@@ -33,6 +35,11 @@ export const SingleCtaLink = (props: Props): ReactElement => { | |||
}} | |||
> | |||
{props?.text || Config.taskDefaults.defaultCallToActionText} | |||
{props.iconName && ( | |||
<Icon className="text-green usa-icon--size-3 margin-left-1 padding-bottom-05"> | |||
{props.iconName} |
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.
Not something you chose, but I think it's super odd that we specify the icon type by passing a string as a child. I kinda hate this.
|
||
export const RaffleBingoPaginator = (props: Props): ReactElement => { | ||
const [stepIndex, setStepIndex] = useState(0); | ||
const [task, setTask] = useState<Task>(); |
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.
[sand] It might be helpful to rename this state variable to better differentiate this from the task prop being passed into this component.
These objects are Task
s but the way we're treating them is really steps in the context of a larger task.
const [task, setTask] = useState<Task>(); | |
const [step, setStep] = useState<Task>(); |
getRaffleBingoTask(stepIndex).then((task) => { | ||
setTask(task); | ||
}); |
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.
[sand] For the same reason as above.
getRaffleBingoTask(stepIndex).then((task) => { | |
setTask(task); | |
}); | |
getRaffleBingoTask(stepIndex).then((step) => { | |
setStep(step); | |
}); |
{ | ||
stepLabel: "Get an ID Number", | ||
fileName: "raffle-license-step-1", | ||
agencyId: "games-of-chance-control-commission", | ||
}, | ||
{ stepLabel: "From Your NJ Municipality", fileName: "raffle-license-step-2", agencyId: "nj-municipality" }, |
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.
[pebble] Things like stepLabel
and agencyId
should probably be content driven. I suggest moving these values into the CMS. Then the 2 filenames representing the steps could be stored in an array.
In RaffleBingoPaginator
, instances where you're referencing RaffleBingoSteps
to get this content could look at the current step.
export const getAgencyName = (agencyId: string): string => { | ||
const issuingAgency = arrayOfTaskAgencies.find((agency) => agency.id === agencyId); | ||
return issuingAgency!.name; | ||
}; |
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.
[boulder] We have a function LookupTaskAgencyById
that's available in taskAgency.ts
which does exactly this. Opt for that function over re-writing the same logic.
I understand the thought here. In the future, if you're adding something that you think would be beneficial elsewhere in the application, it could be an opportunity to write a helper function.
buildUserRoadmap( | ||
generateStartingProfile({ | ||
raffleBingoGames: false, | ||
legalStructureId: "limited-liability-company", |
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.
[dust] If we wanted to be really thorough here, we could be grabbing a random legalStructureId
that is not nonprofit
. This is a common pattern we use in our tests.
Description
Ticket
This pull request resolves #187570392.
Approach
Steps to Test
Yes
for theWill you be holding, operating, or conducting raffles or bingo games?
question and save your response.Apply for a Raffle or Bingo Game License
.Continue
andBack
buttons.Mark as Completed
button, the task progress will change fromNOT STARTED
toCOMPLETED
and a snack bar advising of such, will appear.Notes
Code author checklist
userData
(includingprofileData
,formationData
etc), then I added a new migration filecmsCollections.ts
(see CMS Additions in Engineering Reference/FAQ on the engineering documentation site).env
values in both.env-template
and in Bitwarden