-
Notifications
You must be signed in to change notification settings - Fork 0
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
SCC-4236: Username for My Account 2.0 #397
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
const nyplApiClient = async ({ | ||
apiName = "platform", | ||
version = "v0.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.
Should this be more specific/different ? this is my first time using this so I'm not super familiar with its use across the repo, this didn't seem to break anything though
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.
What do you mean by specific? Also, can you explain what the = {}
in the function signature does?
FWIW this looks fine by me!
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.
@dgcohen maybe you can confirm if this looks kosher? the username validation endpoint requires v0.3, so i suggested emma make the version be a param. at the same time, i thought this was a good opportunity to trim away the duplicated discovery/platform urls in the config.
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 don't know actually how it could be more specific, just generally looking for confirmation that this makes sense from people who wrote/used this before
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 think it's clear enough when you call the function what version is being used since you pass it as an argument. All tests also continue to pass so it shouldn't break anything.
One possible option to make this clearer (maybe?) is to make this a higher-order function. So it can be something like
const baseNyplApiClient = ({
version = "v0.1",
} = {}) => {
return async ({
apiName = "platform",
} = {}) => {
if (CACHE.clients[apiName]) {
return CACHE.clients[apiName]
}
const baseUrl =
appConfig.apiEndpoints[apiName][appEnvironment] + "/" + version
//...
})
})
export const nyplApiClientv01 = baseNyplApiClient()
export const nyplApiClientv03 = baseNyplApiClient({ version: "v0.3" })
And then call either function like they're being called now such as const platformClient = await nyplApiClientv03()
.
Caveat: the syntax might be off and I haven't tested this. If you like this approach, do it in another PR because of its use across the repo. But what do you think, @charmingduchess ?
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 look of that!
I just noticed that with this updated client factory, we will need to update the cache to include the version in the cache key
export const StatusBanner = ({ status, statusMessage }: StatusBannerProps) => { | ||
const bannerContent = ( | ||
<div style={{ alignItems: "center" }}> | ||
{status === "failure" ? ( |
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.
Can you pull out this conditional logic so it's not in nested ternaries, and illustrates the possibilities more clearly? Something along the lines of...
let statusComponent
if (status === 'success'){
statusComponent = ...
} if (status === 'failure && statusMessage){
statusComponent = ...
else {
statusComponent = }
etc
potentially also with some descriptive variable names that point to when a statusMessage
might be there or not
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.
Lmk if this is better– I do think, since the parent component is responsible for passing the statusMessage
, and there's several potential parents and reasons for passing a specific message, any explanation or indication of what that message will be should also live on the parent component if that makes sense
</Text> | ||
</Flex> | ||
|
||
{isLoading ? ( |
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.
Similar request here. Can you replace this nested ternary with a conditional outside of the return statement so it's a easier to scan which components get rendered when?
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.
Again lmk if this is better/ more readable
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.
Same note here - the extraction is good, but the nested ternary itself is hard to read. Can you replace it?
const nyplApiClient = async ({ | ||
apiName = "platform", | ||
version = "v0.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.
What do you mean by specific? Also, can you explain what the = {}
in the function signature does?
FWIW this looks fine by me!
const nyplApiClient = async ({ | ||
apiName = "platform", | ||
version = "v0.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.
@dgcohen maybe you can confirm if this looks kosher? the username validation endpoint requires v0.3, so i suggested emma make the version be a param. at the same time, i thought this was a good opportunity to trim away the duplicated discovery/platform urls in the config.
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 this extraction 🌝
Your changes were saved. | ||
</Text> | ||
)} | ||
{status === "failure" |
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 definitely better, but I'd really like to have this nested ternary replaced. Its ok if you end up with a conditional that seems redundant or repetitive, ie if failure && statusMessage
, if failure && !statusMessage
and if !failure
. the conditional logic doesn't have to be inline of the return statement. in fact, it might have to come out.
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.
Okay ternary gone but I'd like to register my dissent... I think this makes it less readable both here and in the username form
</Text> | ||
</Flex> | ||
|
||
{isLoading ? ( |
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.
Same note here - the extraction is good, but the nested ternary itself is hard to read. Can you replace it?
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.
NOTE: the deletion process is still up for debate. As it is right now, the user can submit an empty string which works the same as using the trash icon to officially delete
What's the debate? That there are two approaches for the same outcome?
This looks really good. There's the alignment issue in the description property when the username edits and I also left other comments.
await client.put(`patrons/${patronId}`, { | ||
varFields: [{ fieldTag: "u", content: newUsername }], | ||
}) |
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 a general problem but I don't think it's a problem we'll encounter. This risks having race conditions if the username is available and then a separate request is made to update the patron with that username. If another patron is in the process of creating an account or updating their own username to the same one, then we'll have this race condition and error. This will be caught regardless on line 66 and also, there's really not much we can do anyway.
What you have here is fine, but wanted to point it out as possible error as an FYI in case it ever does come up.
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.
Okay good to note
pages/api/account/helpers.ts
Outdated
// Username taken but not an error, returns a message. | ||
return { status: 200, message: "Username taken" } | ||
} else { | ||
return { status: 500, message: "Username update failed" } |
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.
Why not throw an error here and return the error object on line 67?
color: "ui.link.primary !important", | ||
textDecorationColor: "ui.link.primary !important", |
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.
Two things:
- I'm not sure this works, or rather, this should not work. I have not successfully added a design token value with
!important
. So this should be updated to"var(--nypl-colors-ui-link-primary) !important"
. - The above point should be resolved if you use the default style. Links in the negative
Banner
are meant to be red:
Are they blue in the designs?
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.
They are blue in the designs and this does work but i'll change it
const nyplApiClient = async ({ | ||
apiName = "platform", | ||
version = "v0.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.
I think it's clear enough when you call the function what version is being used since you pass it as an argument. All tests also continue to pass so it shouldn't break anything.
One possible option to make this clearer (maybe?) is to make this a higher-order function. So it can be something like
const baseNyplApiClient = ({
version = "v0.1",
} = {}) => {
return async ({
apiName = "platform",
} = {}) => {
if (CACHE.clients[apiName]) {
return CACHE.clients[apiName]
}
const baseUrl =
appConfig.apiEndpoints[apiName][appEnvironment] + "/" + version
//...
})
})
export const nyplApiClientv01 = baseNyplApiClient()
export const nyplApiClientv03 = baseNyplApiClient({ version: "v0.3" })
And then call either function like they're being called now such as const platformClient = await nyplApiClientv03()
.
Caveat: the syntax might be off and I haven't tested this. If you like this approach, do it in another PR because of its use across the repo. But what do you think, @charmingduchess ?
</List> | ||
<> | ||
{usernameStatus !== "" && ( | ||
<div |
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 think we talked about this in a different PR, check with Clare if the banner message is read. In the Dynamic Banners we say that this wrapper div should always be rendered and have an aria-live
attribute. If Clare signs off on this, let me know so we can update the docs.
}} | ||
value={tempInput || ""} | ||
id="username-input" | ||
labelText="Must be 5-15 characters and use only letters (a-z) and numbers (0-9)" |
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.
The space between the input and label seems too small, right?
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, j added more styling
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.
It's like a mix of the label and helper text on this component I'm not sure how design arrived at 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.
If there is a default that makes sense functionally (like the helper text) coming from reservoir that deviates from the designs in a small way, can you ask Apoorva if it's alright to use the default instead of overriding?
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.
Okay, I see what's going on. I thought this was the helperErrorText because it was below the input field but you're reversing the order on line 117. I don't understand why this was done.
You can try target the helperErrorText for styling purposes. Can you try having the text value in the helper text prop and invalid text prop? Then you can update the color with the isInvalid
prop.
My concern is that a screenreader would read the "Must be 5-15 characters and use only letters (a-z) and numbers (0-9)" text but that's not necessarily clear that it's for the username. So a hidden "username" label, I think, is better. Let's chat about this.
flexDirection: "column-reverse", | ||
label: { | ||
fontWeight: "400", | ||
color: error ? "ui.error.primary" : "ui.black", |
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 should happen automatically when isInvalid
is true. Or is this because the additional !validateUsername
check below affects the text color?
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 because the label and the invalid text are the same copy, and render in the same place (below the field). Since that's not how this component expects to work, I'm manually making the label text become the invalid text when needed
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.
Can you use helperText
here instead of labelText
? It looks like the helper text is automatically with the correct styling for this.
* Within this form, the user NOT having a username is represented by: username = null, so the | ||
* empty string is an invalid username. | ||
*/ | ||
const [input, setInput] = useState( |
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.
Can you rename this variable to usernameInSierra
? It seems like it's not actually being updated as part of the controlled text input.
const client = await sierraClient() | ||
if (newUsername === "") { | ||
const client = await sierraClient() | ||
await client.put(`patrons/${patronId}`, { |
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.
Does this return a response to verify it was successful? Or is it a quiet "there was no error so it's good" validation?
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 just 200 if successful, no body
} | ||
|
||
const successContent = ( | ||
<Text marginBottom={0} color="ui.black !important"> |
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.
Just to verify, the design token with the !important
flag does work?
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
color: "ui.link.primary !important", | ||
textDecorationColor: "var(--nypl-colors-ui-link-primary) !important", |
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 both works, great, but pick one for consistency, either the design token or the CSS var.
|
||
fireEvent.click(screen.getByRole("button", { name: /save changes/i })) | ||
|
||
await waitFor(() => expect(fetch).toHaveBeenCalledTimes(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.
The second expect is getMostUpdatedSierraAccountData
, correct?
}} | ||
value={tempInput || ""} | ||
id="username-input" | ||
labelText="Must be 5-15 characters and use only letters (a-z) and numbers (0-9)" |
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.
Okay, I see what's going on. I thought this was the helperErrorText because it was below the input field but you're reversing the order on line 117. I don't understand why this was done.
You can try target the helperErrorText for styling purposes. Can you try having the text value in the helper text prop and invalid text prop? Then you can update the color with the isInvalid
prop.
My concern is that a screenreader would read the "Must be 5-15 characters and use only letters (a-z) and numbers (0-9)" text but that's not necessarily clear that it's for the username. So a hidden "username" label, I think, is better. Let's chat about this.
) | ||
|
||
const notEditingView = ( | ||
<Flex alignItems="center" marginTop={{ base: "unset", md: "-xs" }}> |
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.
Did not know or think that -xs
would work.
const [isEditing, setIsEditing] = useState(false) | ||
const [error, setError] = useState(false) | ||
/** | ||
* In Sierra, the user NOT having a username is represented by the empty string: username = "". |
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.
Thanks for adding this comment here.
I'd like to also see this elsewhere but I don't know where yet...
Ticket:
Resolves JIRA ticket SCC-4236.
This PR does the following:
UsernameForm
to profile headerapi/account/username
route to validate new patron usernamesStatusBanner
NOTE: the deletion process is still up for debate. As it is right now, the user can submit an empty string which works the same as using the trash icon to officially delete
**** Out of scope things that also happened in this PR: ****
src/config
(it's a duplicate of "platform")nyplApiClient
to take an api version (defaults tov0.1
)validations/username
inv0.3
How has this been tested?
Accessibility concerns or updates
Checklist: