-
Notifications
You must be signed in to change notification settings - Fork 679
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
[PWA-336] Cart/Checkout Input Errors UI cases #2495
Conversation
|
Performance Test Results The following fails have been reported by WebpageTest. These numbers indicates a possible performance issue with the PR which requires further manual testing to validate. https://pr-2495.pwa-venia.com : LH Performance Expected 0.85 Actual 0.57, LH Best Practices Expected 1 Actual 0.92, WPT Cache Expected 90 Actual 88 |
{ called: setShippingAddressCalled, loading: isSetShippingLoading } | ||
{ | ||
called: setShippingAddressCalled, | ||
error: setShippingAddressError, |
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 a bit counterintuitive to see setShippingAddressCalled
and setShippingAddressError
used as object names, since it's our most common format for naming functions.
Consider calledSetShippingAddress
or errorSettingShippingAddress
, I suppose?
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 pattern is used all over the place. If we want to switch it up let's make sure everyone is aware.
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.
Renamed in 57ab1a7.
@@ -27,6 +27,10 @@ | |||
padding: 0.75rem 1rem; | |||
} | |||
|
|||
.error { | |||
color: rgb(var(--venia-error)); |
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'm not sure this is a property anymore.
- See
tokens.css
for new tokens - If you replace this one, make sure to replace all of the others as well
color: rgb(var(--venia-error)); | |
color: rgb(var(--venia-global-color-error)); |
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.
@jimbo - that token doesn't seem to exist. Would you like me to add it, or pick one of the --venia-global-color-red-xxx
values?
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.
Strike that, we cut the release branch before design tokens landed, so the new tokens are not available. Going to leave this as-is, and we'll resolve when we merge release back into develop
.
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.
Ah, my mistake. I assumed this was targeting develop
but it's actually targeting the release branch. Carry on. 👍
handleOnSubmit, | ||
handleZipChange, | ||
isSetShippingLoading | ||
} = talonProps; | ||
|
||
const classes = mergeClasses(defaultClasses, props.classes); | ||
|
||
const errorElement = errorMessage ? ( | ||
<span className={classes.error}>{errorMessage}</span> |
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 might have liked to see a FormError
component for form-level errors, since I imagine the presentation will be nearly identical for all of them. Such a component could render null
when props.message
was empty, too, cutting down on a lot of the boilerplate.
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.
Added FormError
component in 57ab1a7, and changed all usages in this PR to use it (where it didn't dramatically alter the UX).
I thought we had discussed deferring a dedicated component to a followup Story, but I've gone ahead and implemented one in this scope.
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. I apologize if we backtracked on an agreement. It's not the duplication of code that bothered me; I just saw that we were adding new classnames to a lot of form components as a result of the new error elements, and thought it'd be nice to avoid that churn. Glad to have a component now.
@@ -39,6 +47,30 @@ export const useCustomerForm = props => { | |||
} | |||
}, [getCustomerError]); | |||
|
|||
useEffect(() => { | |||
if (createCustomerAddressError || updateCustomerAddressError) { | |||
errorRef.current.scrollIntoView({ |
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 scrollIntoView()
is the right choice here, but I don't think it belongs in the talon.
The only other place we use this, PaymentsFormItems
, has a talon of its own as well, but the component is the one to call scrollIntoView
because this kind of effect is really just presentational. There's very little logic we put in the component rather than the talon, but I think this qualifies.
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.
Moved this logic to FormError
and put scroll and ref logic in component instead of talon (57ab1a7).
# Conflicts: # packages/peregrine/lib/talons/CartPage/ProductListing/EditModal/__tests__/useProductForm.spec.js # packages/peregrine/lib/talons/CartPage/ProductListing/EditModal/useProductForm.js
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 looks good - I don't see anything worth holding up the PR. Would be nice to have a utility function, and perhaps move the error scrolling to the component but otherwise 👍
if (setShippingAddressError.graphQLErrors) { | ||
derivedError = setShippingAddressError.graphQLErrors | ||
.map(({ message }) => message) | ||
.join(', '); | ||
} else { | ||
derivedError = setShippingAddressError.message; | ||
} |
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 smell a utility function!
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.
We are probably not going to render Apollo errors going forward, since they can't be trusted to be human readable. I have moved this logic to a new FormError
component which should reduce the boiler plate though, but you still gotta handroll this logic if you don't like the presentation of that component.
] = useMutation(updateConfigurableOptionsMutation); | ||
|
||
const isSaving = | ||
(updateQuantityCalled && updateQuantityLoading) || | ||
(updateConfigurableCalled && updateConfigurableLoading); | ||
|
||
useEffect(() => { | ||
if (formApi) { | ||
formApi.setValue('quantity', cartItem.quantity); |
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 was this doing and why is it gone now?
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 intention was to make sure quantity was updated between renders, but two things made it obsolete:
- I started un-mounting between renders, so this wasn't needed
- This was fixed in the Quantity component in the same way, so this was redundant anyway (even if we didn't unmount between renders)
…nd rendering error messages.
@@ -40,6 +42,7 @@ const ShippingForm = props => { | |||
return ( | |||
<Fragment> | |||
<h3 className={classes.formTitle}>Destination</h3> | |||
<FormError errors={formErrors} /> |
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.
You don't have to do this, but consider just rendering the FormError
element as the first child of the Form
element, rather than wrapping these forms in fragments. Might help if the form wants to have the form error participate in its layout.
.map(({ message }) => message) | ||
.join(', '); | ||
} else { | ||
// A non-GraphQL error occurred. | ||
applyErrorMessage - applyError.message; | ||
derivedErrorMessage = errorTarget.message; |
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.
Do we need to handle network errors? They'd be on the target as errorTarget.networkError
.
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.
Discussed offline - I am for moving this sort of logic into a utility that is used by useFormError
and by any other components that need to turn apollo errors into a string.
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 was going to suggest that too, 💯 agree
@@ -23,7 +23,7 @@ jest.mock('../shippingForm', () => 'ShippingForm'); | |||
jest.mock('../shippingRadios', () => 'ShippingRadios'); | |||
|
|||
test('renders description and confirm link w/o shipping address set', () => { | |||
useLazyQuery.mockReturnValue([ | |||
useQuery.mockReturnValue([ |
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'm surprised this is working - useQuery
just returns an object, not an array like useLazyQuery
does.
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.
Very odd. I haven't full covered this with tests yet, so I'll be sure to investigate this and update the mock to accurately reflect the implementation of useQuery.
|
||
if (applyError) { | ||
if (applyError.graphQLErrors) { | ||
if (errorTarget.graphQLErrors) { |
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.
OOPS - as we just discovered, graphQLErrors
is an array. All these checks will need to be updated to length
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.
Great catch! Added this check to FormError
, and we'll address the other areas once we create a utility.
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.
Looks awesome! Great organization and consistency.
I had one question about a unit test mock that I'm surprised doesn't break things.
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.
Good - we will push the fixing of the other places missing the length check to a follow up ticket.
QA Approved. |
Description
After Cart/Checkout is done we should make a general pass over the error states to make sure we represent them correctly according to UX desires and the style guide.
Cases to consider:
h5. Open Questions:
Summary of GraphQL Operations that may have error state: https://wiki.corp.magento.com/pages/viewpage.action?pageId=144314396
Related Issue
Acceptance
Verification Stakeholders
Specification
Verification Steps
This guy is going to be a little difficult to test, it's quite tricky to simulate errors. You might be able to go offline and use network errors (we've discovered this actually doesn't work in most places), but modifying code to break the queries or mutations is what I did. I'll outline the components touched, and the verification steps are all the same; simulate an error somehow and verify output. I have also included screenshots in the HLD for UX, to make visual review easier.
Screenshots / Screen Captures (if appropriate)
See HLD for tons of screenshots.
Checklist