From 5623a502ebf88c35d27452ada396d26ebd3dcc93 Mon Sep 17 00:00:00 2001 From: Connie Liu <139280159+coliu-akamai@users.noreply.github.com> Date: Tue, 5 Dec 2023 16:36:40 -0500 Subject: [PATCH] refactor: [M3-7532] - Payment Method Row Storybook v7 Story (#9958) * props and export cleanup * add tests, start story * starting story * stories for storybook * update comments * Added changeset: Payment Method Row V7 story migration * address feedback * update key * feedback --- .../pr-9958-tech-stories-1701721635562.md | 5 + .../DeletePaymentMethodDialog.test.tsx | 46 +++++++ .../DeletePaymentMethodDialog.tsx | 14 +- .../PaymentMethodRow.stories.mdx | 130 ------------------ .../PaymentMethodRow.stories.tsx | 78 +++++++++++ .../PaymentMethodRow.test.tsx | 2 +- .../PaymentMethodRow/PaymentMethodRow.tsx | 16 ++- .../ThirdPartyPayment.test.tsx | 2 +- .../PaymentMethodRow/ThirdPartyPayment.tsx | 23 ++-- .../src/components/PaymentMethodRow/index.ts | 1 - .../PaymentInfoPanel/PaymentInformation.tsx | 2 +- .../PaymentInfoPanel/PaymentMethods.tsx | 2 +- 12 files changed, 163 insertions(+), 158 deletions(-) create mode 100644 packages/manager/.changeset/pr-9958-tech-stories-1701721635562.md create mode 100644 packages/manager/src/components/PaymentMethodRow/DeletePaymentMethodDialog.test.tsx delete mode 100644 packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.stories.mdx create mode 100644 packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.stories.tsx delete mode 100644 packages/manager/src/components/PaymentMethodRow/index.ts diff --git a/packages/manager/.changeset/pr-9958-tech-stories-1701721635562.md b/packages/manager/.changeset/pr-9958-tech-stories-1701721635562.md new file mode 100644 index 00000000000..fc782cdad4e --- /dev/null +++ b/packages/manager/.changeset/pr-9958-tech-stories-1701721635562.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tech Stories +--- + +Payment Method Row V7 story migration ([#9958](https://github.com/linode/manager/pull/9958)) diff --git a/packages/manager/src/components/PaymentMethodRow/DeletePaymentMethodDialog.test.tsx b/packages/manager/src/components/PaymentMethodRow/DeletePaymentMethodDialog.test.tsx new file mode 100644 index 00000000000..8d9f0cc9d47 --- /dev/null +++ b/packages/manager/src/components/PaymentMethodRow/DeletePaymentMethodDialog.test.tsx @@ -0,0 +1,46 @@ +import { fireEvent } from '@testing-library/react'; +import * as React from 'react'; + +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { DeletePaymentMethodDialog } from './DeletePaymentMethodDialog'; + +const props = { + error: 'some error', + loading: false, + onClose: vi.fn(), + onDelete: vi.fn(), + open: true, + paymentMethod: undefined, +}; + +describe('Delete Payment Method Dialog', () => { + it('renders the delete payment method dialog', () => { + const screen = renderWithTheme(); + + const headerText = screen.getByText('Delete Payment Method'); + expect(headerText).toBeVisible(); + + const deleteText = screen.getByText( + 'Are you sure you want to delete this payment method?' + ); + expect(deleteText).toBeVisible(); + + const buttons = screen.getAllByRole('button'); + expect(buttons?.length).toBe(3); + }); + + it('calls the corresponding functions when buttons are clicked', () => { + const screen = renderWithTheme(); + + const deleteButton = screen.getByText('Delete'); + expect(deleteButton).toBeVisible(); + fireEvent.click(deleteButton); + expect(props.onDelete).toHaveBeenCalled(); + + const cancelButton = screen.getByText('Cancel'); + expect(cancelButton).toBeVisible(); + fireEvent.click(cancelButton); + expect(props.onClose).toHaveBeenCalled(); + }); +}); diff --git a/packages/manager/src/components/PaymentMethodRow/DeletePaymentMethodDialog.tsx b/packages/manager/src/components/PaymentMethodRow/DeletePaymentMethodDialog.tsx index 8b60bd420a9..204e2050e3a 100644 --- a/packages/manager/src/components/PaymentMethodRow/DeletePaymentMethodDialog.tsx +++ b/packages/manager/src/components/PaymentMethodRow/DeletePaymentMethodDialog.tsx @@ -1,16 +1,16 @@ import { PaymentMethod } from '@linode/api-v4/lib/account/types'; import { Theme } from '@mui/material/styles'; -import { makeStyles } from '@mui/styles'; import * as React from 'react'; +import { makeStyles } from 'tss-react/mui'; import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog'; import CreditCard from 'src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/CreditCard'; import { ActionsPanel } from '../ActionsPanel/ActionsPanel'; import { Grid } from '../Grid'; -import ThirdPartyPayment from './ThirdPartyPayment'; +import { ThirdPartyPayment } from './ThirdPartyPayment'; -export const useStyles = makeStyles((theme: Theme) => ({ +export const useStyles = makeStyles()((theme: Theme) => ({ container: { flexWrap: 'nowrap', marginTop: theme.spacing(1), @@ -30,9 +30,9 @@ interface Props { paymentMethod: PaymentMethod | undefined; } -export const DeletePaymentMethodDialog: React.FC = (props) => { +export const DeletePaymentMethodDialog = React.memo((props: Props) => { const { error, loading, onClose, onDelete, open, paymentMethod } = props; - const classes = useStyles(); + const { classes } = useStyles(); const actions = ( = (props) => { ); -}; - -export default React.memo(DeletePaymentMethodDialog); +}); diff --git a/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.stories.mdx b/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.stories.mdx deleted file mode 100644 index 2a17e4de74c..00000000000 --- a/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.stories.mdx +++ /dev/null @@ -1,130 +0,0 @@ -import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs'; -import PaymentMethodRow from 'src/components/PaymentMethodRow/PaymentMethodRow'; -import { paymentMethodFactory } from 'src/factories'; - - - -export const onDelete = () => {}; - -export const supportedCreditCards = [ - 'Visa', - 'MasterCard', - 'American Express', - 'Discover', - 'JCB', - 'Other', -]; - -export const CreditCards = () => - supportedCreditCards.map((creditCard) => ( - - )); - -export const Template = (args) => ; - -# Payment Method Row - -## Credit Cards - - - - - - - -## Google Pay - - - - {Template.bind({})} - - - - - -## PayPal - - - - {Template.bind({})} - - - - - -## Component API - - - - {Template.bind({})} - - - - diff --git a/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.stories.tsx b/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.stories.tsx new file mode 100644 index 00000000000..55195da6b58 --- /dev/null +++ b/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.stories.tsx @@ -0,0 +1,78 @@ +import { CardType } from '@linode/api-v4'; +import { action } from '@storybook/addon-actions'; +import React from 'react'; + +import { paymentMethodFactory } from 'src/factories'; + +import { PaymentMethodRow } from './PaymentMethodRow'; + +import type { Meta, StoryObj } from '@storybook/react'; + +type Story = StoryObj; + +const onDelete = action('onDelete'); + +const supportedCreditCards: (CardType | undefined)[] = [ + 'Visa', + 'MasterCard', + 'American Express', + 'Discover', + 'JCB', + undefined, +]; + +const CreditCardExamples = () => { + const paymentMethods = supportedCreditCards.map((creditCard) => ( + + )); + + return <>{paymentMethods}; +}; + +export const CreditCards: Story = { + render: () => , +}; + +export const GooglePay: Story = { + render: (args) => , +}; + +export const PayPal: Story = { + render: (args) => ( + + ), +}; + +const meta: Meta = { + args: { + onDelete, + paymentMethod: paymentMethodFactory.build({ + data: { + card_type: 'Visa', + }, + type: 'google_pay', + }), + }, + component: PaymentMethodRow, + title: 'Components/Payment Method Row', +}; + +export default meta; diff --git a/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.test.tsx b/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.test.tsx index 50f7917bff6..486d083a4ac 100644 --- a/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.test.tsx +++ b/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.test.tsx @@ -8,7 +8,7 @@ import { paymentMethodFactory } from 'src/factories'; import BillingSummary from 'src/features/Billing/BillingPanels/BillingSummary'; import { renderWithTheme } from 'src/utilities/testHelpers'; -import PaymentMethodRow from './PaymentMethodRow'; +import { PaymentMethodRow } from './PaymentMethodRow'; vi.mock('@linode/api-v4/lib/account', async () => { const actual = await vi.importActual('@linode/api-v4/lib/account'); diff --git a/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.tsx b/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.tsx index 6b3f8201470..fe28b09b3dc 100644 --- a/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.tsx +++ b/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.tsx @@ -13,14 +13,24 @@ import { Paper } from 'src/components/Paper'; import CreditCard from 'src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/CreditCard'; import { queryKey } from 'src/queries/accountPayment'; -import ThirdPartyPayment from './ThirdPartyPayment'; +import { ThirdPartyPayment } from './ThirdPartyPayment'; interface Props { + /** + * Function called when the delete button in the Action Menu is pressed. + */ onDelete: () => void; + /** + * Payment method type and data. + */ paymentMethod: PaymentMethod; } -const PaymentMethodRow = (props: Props) => { +/** + * The `PaymentMethodRow` displays the given payment method and supports various actions for each payment method. It can be used + * for credit cards, Google Pay, and PayPal. + */ +export const PaymentMethodRow = (props: Props) => { const theme = useTheme(); const { onDelete, paymentMethod } = props; const { is_default, type } = paymentMethod; @@ -125,5 +135,3 @@ const PaymentMethodRow = (props: Props) => { ); }; - -export default PaymentMethodRow; diff --git a/packages/manager/src/components/PaymentMethodRow/ThirdPartyPayment.test.tsx b/packages/manager/src/components/PaymentMethodRow/ThirdPartyPayment.test.tsx index 00da257b2b8..592bb6a2bca 100644 --- a/packages/manager/src/components/PaymentMethodRow/ThirdPartyPayment.test.tsx +++ b/packages/manager/src/components/PaymentMethodRow/ThirdPartyPayment.test.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { paymentMethodFactory } from 'src/factories'; import { renderWithTheme } from 'src/utilities/testHelpers'; -import ThirdPartyPayment from './ThirdPartyPayment'; +import { ThirdPartyPayment } from './ThirdPartyPayment'; it('Displays credit card type and last four digits when payment method is Google Pay', () => { const googlePayPaymentMethod = paymentMethodFactory.build({ diff --git a/packages/manager/src/components/PaymentMethodRow/ThirdPartyPayment.tsx b/packages/manager/src/components/PaymentMethodRow/ThirdPartyPayment.tsx index ab78f6318ec..a705ea605be 100644 --- a/packages/manager/src/components/PaymentMethodRow/ThirdPartyPayment.tsx +++ b/packages/manager/src/components/PaymentMethodRow/ThirdPartyPayment.tsx @@ -1,16 +1,19 @@ -import { PaymentMethod, ThirdPartyPayment } from '@linode/api-v4/lib/account'; -import { Box } from 'src/components/Box'; -import { Theme } from '@mui/material/styles'; +import { + ThirdPartyPayment as _ThirdPartyPayment, + PaymentMethod, +} from '@linode/api-v4/lib/account'; +import { Theme, useTheme } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; -import { makeStyles, useTheme } from '@mui/styles'; import * as React from 'react'; +import { makeStyles } from 'tss-react/mui'; import GooglePayIcon from 'src/assets/icons/payment/googlePay.svg'; import PayPalIcon from 'src/assets/icons/payment/payPal.svg'; +import { Box } from 'src/components/Box'; import { Typography } from 'src/components/Typography'; import CreditCard from 'src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/CreditCard'; -const useStyles = makeStyles((theme: Theme) => ({ +const useStyles = makeStyles()((theme: Theme) => ({ icon: { // https://stackoverflow.com/questions/57516373/image-stretching-in-flexbox-in-safari alignItems: 'center', @@ -62,18 +65,18 @@ export const renderThirdPartyPaymentBody = (paymentMethod: PaymentMethod) => { } }; -export const getIcon = (paymentMethod: ThirdPartyPayment) => { +export const getIcon = (paymentMethod: _ThirdPartyPayment) => { return thirdPartyPaymentMap[paymentMethod].icon; }; -export const TPP: React.FC = (props) => { +export const ThirdPartyPayment = (props: Props) => { const { paymentMethod } = props; - const classes = useStyles(); + const { classes } = useStyles(); const theme = useTheme(); const matchesSmDown = useMediaQuery(theme.breakpoints.down('md')); - const Icon = getIcon(paymentMethod.type as ThirdPartyPayment); + const Icon = getIcon(paymentMethod.type as _ThirdPartyPayment); return ( <> @@ -91,5 +94,3 @@ export const TPP: React.FC = (props) => { ); }; - -export default TPP; diff --git a/packages/manager/src/components/PaymentMethodRow/index.ts b/packages/manager/src/components/PaymentMethodRow/index.ts deleted file mode 100644 index f71c3bc8339..00000000000 --- a/packages/manager/src/components/PaymentMethodRow/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './PaymentMethodRow'; diff --git a/packages/manager/src/features/Billing/BillingPanels/PaymentInfoPanel/PaymentInformation.tsx b/packages/manager/src/features/Billing/BillingPanels/PaymentInfoPanel/PaymentInformation.tsx index 624daa8efa5..80212410af1 100644 --- a/packages/manager/src/features/Billing/BillingPanels/PaymentInfoPanel/PaymentInformation.tsx +++ b/packages/manager/src/features/Billing/BillingPanels/PaymentInfoPanel/PaymentInformation.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { useQueryClient } from 'react-query'; import { useHistory, useRouteMatch } from 'react-router-dom'; -import DeletePaymentMethodDialog from 'src/components/PaymentMethodRow/DeletePaymentMethodDialog'; +import { DeletePaymentMethodDialog } from 'src/components/PaymentMethodRow/DeletePaymentMethodDialog'; import { Typography } from 'src/components/Typography'; import { PaymentMethods } from 'src/features/Billing/BillingPanels/PaymentInfoPanel/PaymentMethods'; import { queryKey } from 'src/queries/accountPayment'; diff --git a/packages/manager/src/features/Billing/BillingPanels/PaymentInfoPanel/PaymentMethods.tsx b/packages/manager/src/features/Billing/BillingPanels/PaymentInfoPanel/PaymentMethods.tsx index aa10e9aa856..515b654bfa2 100644 --- a/packages/manager/src/features/Billing/BillingPanels/PaymentInfoPanel/PaymentMethods.tsx +++ b/packages/manager/src/features/Billing/BillingPanels/PaymentInfoPanel/PaymentMethods.tsx @@ -4,7 +4,7 @@ import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { CircleProgress } from 'src/components/CircleProgress'; -import PaymentMethodRow from 'src/components/PaymentMethodRow'; +import { PaymentMethodRow } from 'src/components/PaymentMethodRow/PaymentMethodRow'; import { Typography } from 'src/components/Typography'; import { getAPIErrorOrDefault } from 'src/utilities/errorUtils';