-
Notifications
You must be signed in to change notification settings - Fork 396
upcoming: [UIE-9823] - Implement the Contact Sales Drawer for Marketplace products #13368
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
base: develop
Are you sure you want to change the base?
Conversation
|
The form initializes additional email to ['']. API Spec expects: ["email1@akamai.com"] or omit if empty |
|
One observation in api spec doc there is a typo API Spec shows: "parner_name": "Akamai" (typo - missing 't'). Please get this corrected from backend team. |
| ? getAPIErrorOrDefault(error)?.[0].reason | ||
| : "Oops! Something went wrong and we couldn't send your contacts. Please try again in a moment, or refresh the page."; | ||
| enqueueSnackbar(errorMessage, { variant: 'error' }); | ||
| onClose(); |
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.
On api error do we need to close the drawer?
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 snackbar will only be shown if we close the drawer.
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 might not want to close the drawer on API errors since that would require the user to re-enter their details, which isn't ideal. Typically, in CM, we don't close drawers when an API call fails; instead, we usually show an error notification banner within the drawer itself
I also noticed that form data persists when reopening the drawer, but navigating between tabs within the details page resets the form data. I think the drawer should always open with an empty state
| return ( | ||
| <Drawer | ||
| data-testid="contact-sales-drawer" | ||
| onClose={() => { |
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.
Instead of resetting the form onClose which can cause visual glitches if the form resets while the drawer is animating closed. we can use onTransitionExited={() => reset()}
| @@ -0,0 +1,590 @@ | |||
| import { yupResolver } from '@hookform/resolvers/yup'; | |||
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.
No unit tests for ContactSalesDrawer component
| } from '@linode/ui'; | ||
| import { createPartnerReferralSchema } from '@linode/validation'; | ||
| import { createFilterOptions, Grid } from '@mui/material'; | ||
| import { enqueueSnackbar } from 'notistack'; |
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.
mostly this pattern is follwed in the codebase import { useSnackbar } from 'notistack';
const { enqueueSnackbar } = useSnackbar(); . Direct import works but is inconsistent with codebase conventions.
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 believe both the implementations are supported. Importing enqueueSnackBar has been followed in multiple places like here: https://github.com/linode/manager/blob/develop/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeEntityDetailFooter.tsx#L6
| render={({ field }) => { | ||
| const emailErrors = errors.additional_emails; | ||
| return ( | ||
| <MultipleIPInput |
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.
Using MultipleIPInput for email addresses works but is semantically confusing. looked like the component was designed for IP addresses. Consider:
Adding a comment explaining this reuse
Or see if there is possibility of MultipleEmailInput wrapper
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 did agree that building a custom component for this use case is not justified as of yet. Will add a comment for clarity though
| <MultipleIPInput | ||
| buttonText="Add email address" | ||
| className={ | ||
| field.value?.length === 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.
can we use a constant here : MAX_ADDITIONAL_EMAILS = 2
|
|
||
| const { | ||
| control, | ||
| formState: { errors, isSubmitting }, |
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 the form is submitted While isSubmitting is used to disable form fields. Is this is correct submitting state. Can we check with ux on this.
| const { classes } = useStyles(); | ||
| const { onClose, open, partnerName, productName } = props; | ||
|
|
||
| const { data: profile } = useProfile(); |
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.
Are we sure this data is not asynchronous and will always be available when drawer is opened? Basically do you this we should handle async nature of this data?
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 useProfile() api is loaded on login so we will always have the data.
| @@ -0,0 +1,590 @@ | |||
| import { yupResolver } from '@hookform/resolvers/yup'; | |||
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.
Missing Notice for in-form api returned errors
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 API needs to send the field parameter in errors if we want to show different error components for in-form errors. Currently, the error response from this api doesn't return this field parameter
| open={open} | ||
| title={`Contact ${partnerName} sales`} | ||
| > | ||
| <form onSubmit={onSubmit}> |
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.
Pendo id's missing on all the elements. Please add attributes as mentioned in figma provided by pendo team.
| id={id} | ||
| onClick={handleClick} | ||
| onKeyPress={handleKeyPress} | ||
| role={role} |
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.
| role={role} | |
| role={role ?? (onClick ? 'button' : undefined)} |
I think it would be better to automatically set role="button" when onClick is provided, rather than requiring consumers to manually pass a role. This helps ensure that clickable cards are exposed as interactive to screen readers by default while still allowing flexibility to override the role when different semantics are needed (for eg., radio-button behavior with onClick)
| heading={productName} | ||
| onClick={onClick} | ||
| renderIcon={renderHeader} | ||
| role="button" |
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.
| role="button" |
| secondaryButtonProps={{ | ||
| label: 'Cancel', | ||
| onClick: onClose, | ||
| buttonType: 'outlined', |
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.
| ? getAPIErrorOrDefault(error)?.[0].reason | ||
| : "Oops! Something went wrong and we couldn't send your contacts. Please try again in a moment, or refresh the page."; | ||
| enqueueSnackbar(errorMessage, { variant: 'error' }); | ||
| onClose(); |
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 might not want to close the drawer on API errors since that would require the user to re-enter their details, which isn't ideal. Typically, in CM, we don't close drawers when an API call fails; instead, we usually show an error notification banner within the drawer itself
I also noticed that form data persists when reopening the drawer, but navigating between tabs within the details page resets the form data. I think the drawer should always open with an empty state
dwiley-akamai
left a comment
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'll need an Upcoming Features changeset for each package touched
| } | ||
| }); | ||
|
|
||
| it('renders an removable text field on click of the "Add Email Address" button', 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.
| it('renders an removable text field on click of the "Add Email Address" button', async () => { | |
| it('renders a removable text field on click of the "Add Email Address" button', async () => { |
| Fill the form and our partner's sales team will reach out to | ||
| you |
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.
Shouldn't this have a period at the end? And maybe something like "Complete the form [...]" would make it sound a bit more formal
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 was the phrasing preferred by the tech writing team. Will double check on this
Cloud Manager UI test results🔺 1 failing test on test run #12 ↗︎
Details
TroubleshootingUse this command to re-run the failing tests: pnpm cy:run -s "cypress/e2e/core/firewalls/firewall-landing-page.spec.ts" |
|||||||||||||||||
| */ | ||
| renderVariant?: () => JSX.Element | null; | ||
| /** | ||
| * An optional prop to set the ARIA role of the selection card. |
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.
| * An optional prop to set the ARIA role of the selection card. | |
| * An optional prop to set the ARIA role of the selection card. |
Very minor nit: extra spacing between words
| }} | ||
| > | ||
| Load More... | ||
| Load more offers > |
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 should use chevron-right icon here
| slotProps={{ | ||
| paper: { | ||
| sx: { | ||
| overflow: 'hidden', | ||
| // Set the options width to cover the entire textfield when the drawer is at or above its designed width | ||
| width: { | ||
| sm: '401px', | ||
| xs: '366px', | ||
| }, | ||
| // When the drawer width is less than 445px, expand to drawer width (minus padding offset) on mobile | ||
| '@media (max-width: 445px)': { | ||
| width: 'calc(100vw - 79px)', | ||
| }, | ||
| }, | ||
| }, | ||
| }} |
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.
| slotProps={{ | |
| paper: { | |
| sx: { | |
| overflow: 'hidden', | |
| // Set the options width to cover the entire textfield when the drawer is at or above its designed width | |
| width: { | |
| sm: '401px', | |
| xs: '366px', | |
| }, | |
| // When the drawer width is less than 445px, expand to drawer width (minus padding offset) on mobile | |
| '@media (max-width: 445px)': { | |
| width: 'calc(100vw - 79px)', | |
| }, | |
| }, | |
| }, | |
| }} |
I don't see any effect from these slotProps styles on the phone number field. We usually avoid using media queries directly like this and prefer breakpoints instead. Since I didn't notice any visible impact from the above, this might not be required at all. Could you double-check?
| }} | ||
| options={countryList} | ||
| placeholder="" | ||
| renderOption={({ key, ...props }, option) => ( |
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 like this key prop is unused here
| }} | ||
| title="Additional email addresses" | ||
| tooltip="You can add two additional emails" | ||
| /> |
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.
| control={control} | ||
| name="phone_country_code" | ||
| render={({ field }) => ( | ||
| <Autocomplete |
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.
Screen.Recording.2026-02-10.at.6.42.44.PM.mov
I noticed a slight pixel shift when hovering over this input field. Could we fix this?
| control={control} | ||
| name="country_code" | ||
| render={({ field }) => ( | ||
| <Autocomplete |
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.



Description 📝
Implement the Contact Sales Drawer for Marketplace products
Scope 🚢
Upon production release, changes in this PR will be visible to:
Preview 📷
How to test 🧪
Prerequisites
Verification steps
Author Checklists
As an Author, to speed up the review process, I considered 🤔
👀 Doing a self review
❔ Our contribution guidelines
🤏 Splitting feature into small PRs
➕ Adding a changeset
🧪 Providing/improving test coverage
🔐 Removing all sensitive information from the code and PR description
🚩 Using a feature flag to protect the release
👣 Providing comprehensive reproduction steps
📑 Providing or updating our documentation
🕛 Scheduling a pair reviewing session
📱 Providing mobile support
♿ Providing accessibility support
As an Author, before moving this PR from Draft to Open, I confirmed ✅