Skip to content
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

Payouts: Show bank reference key on payout details page #9886

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
33208a5
Add bank reference key to the deposit details header
Dec 5, 2024
98333d5
Merge branch 'develop' into add/9878-bank-ref-key-payout-details
Dec 5, 2024
1939d4e
Add conditional
Dec 5, 2024
2d5a129
Changelog
Dec 5, 2024
e0f0dd6
CSS adjustments for mobile view
Dec 5, 2024
aea1f35
Css tweak
Dec 5, 2024
a935636
Merge branch 'develop' into add/9878-bank-ref-key-payout-details
Dec 5, 2024
6386ab8
show n/a when ref key not available
Dec 6, 2024
3ace18a
Update test snapshots
Dec 6, 2024
d318c33
Merge branch 'develop' into add/9878-bank-ref-key-payout-details
Dec 6, 2024
735fe94
Add copy button
Dec 6, 2024
e96ba95
Tweak appearance of button and snapshots
Dec 6, 2024
ed00ff5
Merge branch 'develop' into add/9878-bank-ref-key-payout-details
Dec 6, 2024
d1339f9
Merge branch 'develop' into add/9878-bank-ref-key-payout-details
Dec 6, 2024
292bfd9
Change Ref ID font to monospace
Dec 6, 2024
b503845
Merge branch 'develop' into add/9878-bank-ref-key-payout-details
haszari Dec 9, 2024
8bda02c
CSS tweak
Dec 9, 2024
25b87b3
Merge branch 'develop' into add/9878-bank-ref-key-payout-details
Dec 9, 2024
294a44d
Merge branch 'develop' into add/9878-bank-ref-key-payout-details
Dec 11, 2024
34790c9
Merge branch 'develop' into add/9878-bank-ref-key-payout-details
Dec 11, 2024
21b2e01
Move DepositDateItem to its own React FC
Dec 11, 2024
05ee6ca
WIP changes
Dec 11, 2024
4a4720a
Fix brackets
Dec 11, 2024
a159495
Update snapshots
Dec 11, 2024
aa7eb1b
Merge branch 'develop' into add/9878-bank-ref-key-payout-details
Dec 12, 2024
af176bd
Simplify click handler
Dec 12, 2024
0d26f76
Move copy button to component
Dec 12, 2024
5c3c6aa
Add test and label prop
Dec 12, 2024
feb2746
Merge branch 'develop' into add/9878-bank-ref-key-payout-details
Dec 12, 2024
3b4bcc9
Use named exports as per TS guidelines
Dec 13, 2024
8a4f35e
Add comments for props
Dec 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog/add-9878-bank-ref-key-payout-details
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Show Bank reference key on top of the payout details page, whenever available.
64 changes: 64 additions & 0 deletions client/components/copy-button/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* External dependencies
*/
import React, { useState, useEffect, useRef } from 'react';
import { __ } from '@wordpress/i18n';
import classNames from 'classnames';

/**
* Internal dependencies
*/
import './style.scss';

interface CopyButtonProps {
/**
* The text to copy to the clipboard.
*/
textToCopy: string;

/**
* The label for the button. Also used as the aria-label.
*/
label: string;
}

export const CopyButton: React.FC< CopyButtonProps > = ( {
textToCopy,
label,
} ) => {
// useRef() is used to store the timer reference for the setTimeout() function.
const timerRef = useRef< NodeJS.Timeout | null >( null );

// useEffect() is used to clear the timer reference when the component is unmounted.
useEffect( () => {
return () => {
if ( timerRef.current ) {
clearTimeout( timerRef.current );
}
};
}, [] );

const [ copied, setCopied ] = useState( false );

const copyToClipboard = () => {
navigator.clipboard.writeText( textToCopy );
setCopied( true );
timerRef.current = setTimeout( () => {
setCopied( false );
}, 2000 );
};

return (
<button
type="button"
className={ classNames( 'woopayments-copy-button', {
'state--copied': copied,
} ) }
aria-label={ label }
title={ __( 'Copy to clipboard', 'woocommerce-payments' ) }
onClick={ copyToClipboard }
>
<i></i>
</button>
);
};
37 changes: 37 additions & 0 deletions client/components/copy-button/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.woopayments-copy-button {
line-height: 1.2em;
display: inline-flex;
background: transparent;
border: none;
border-radius: 0;
vertical-align: middle;
font-weight: normal;
cursor: pointer;
color: inherit;
margin-left: 2px;
align-items: center;

i {
display: block;
width: 1.2em;
height: 1.2em;
mask-image: url( 'assets/images/icons/copy.svg?asset' );
mask-size: contain;
mask-repeat: no-repeat;
mask-position: center;
background-color: currentColor;

&:hover {
opacity: 0.7;
}

&:active {
transform: scale( 0.9 );
}
}

&.state--copied i {
mask-image: url( 'assets/images/icons/check-green.svg?asset' );
background-color: $studio-green-50;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CopyButton renders the button correctly 1`] = `
<div>
<button
aria-label="Copy bank reference ID to clipboard"
class="woopayments-copy-button"
title="Copy to clipboard"
type="button"
>
<i />
</button>
</div>
`;
70 changes: 70 additions & 0 deletions client/components/copy-button/test/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/** @format **/

/**
* External dependencies
*/
import React from 'react';
import { act, render, screen } from '@testing-library/react';

/**
* Internal dependencies
*/
import { CopyButton } from '..';

describe( 'CopyButton', () => {
it( 'renders the button correctly', () => {
const { container: copyButtonContainer } = render(
<CopyButton
textToCopy="test_bank_reference_id"
label="Copy bank reference ID to clipboard"
/>
);

expect( copyButtonContainer ).toMatchSnapshot();
} );

describe( 'when the button is clicked', () => {
beforeAll( () => {
jest.useFakeTimers();
} );

afterAll( () => {
jest.useRealTimers();
} );

it( 'copies the text to the clipboard and shows copied state', () => {
render(
<CopyButton
textToCopy="test_bank_reference_id"
label="Copy bank reference ID to clipboard"
/>
);

const button = screen.queryByRole( 'button', {
name: /Copy bank reference ID to clipboard/i,
} );

//Mock the clipboard API
Object.assign( navigator, {
clipboard: {
writeText: jest.fn(),
},
} );

act( () => {
button?.click();
} );

expect( navigator.clipboard.writeText ).toHaveBeenCalledWith(
'test_bank_reference_id'
);
expect( button ).toHaveClass( 'state--copied' );

act( () => {
jest.advanceTimersByTime( 2000 );
} );

expect( button ).not.toHaveClass( 'state--copied' );
} );
} );
} );
78 changes: 53 additions & 25 deletions client/deposits/details/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import classNames from 'classnames';
import type { CachedDeposit } from 'types/deposits';
import { useDeposit } from 'data';
import TransactionsList from 'transactions/list';
import { CopyButton } from 'components/copy-button';
import Page from 'components/page';
import ErrorBoundary from 'components/error-boundary';
import { TestModeNotice } from 'components/test-mode-notice';
Expand Down Expand Up @@ -70,7 +71,7 @@ interface SummaryItemProps {
label: string;
value: string | JSX.Element;
valueClass?: string | false;
detail?: string;
detail?: string | JSX.Element;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this align with the types for the component we are overriding (SummaryNumber from Woo core?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original SummaryItemNumber ( child within the summary list ) , does not have a detail prop. It was introduced in the customized SummaryListItem for the Deposit Overview header.

Copy link
Contributor

@haszari haszari Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to this PR, but I think we should avoid inheriting/overriding components like this. We risk depending on implementation details, so our plugin is sensitive to changes in Woo core.

Ideally we'd either:

  • Use components from Woo/upstream as is, with no customisations or overrides.
  • Implement our own self-contained components, with no dependencies.

}

/**
Expand Down Expand Up @@ -102,35 +103,20 @@ const SummaryItem: React.FC< SummaryItemProps > = ( {
</li>
);

interface DepositOverviewProps {
deposit: CachedDeposit | undefined;
interface DepositDateItemProps {
deposit: CachedDeposit;
}

export const DepositOverview: React.FC< DepositOverviewProps > = ( {
deposit,
} ) => {
if ( ! deposit ) {
return (
<InlineNotice icon status="error" isDismissible={ false }>
{ __(
`The deposit you are looking for cannot be found.`,
'woocommerce-payments'
) }
</InlineNotice>
);
}

const isWithdrawal = deposit.type === 'withdrawal';

const DepositDateItem: React.FC< DepositDateItemProps > = ( { deposit } ) => {
let depositDateLabel = __( 'Payout date', 'woocommerce-payments' );
if ( ! deposit.automatic ) {
depositDateLabel = __( 'Instant payout date', 'woocommerce-payments' );
}
if ( isWithdrawal ) {
if ( deposit.type === 'withdrawal' ) {
depositDateLabel = __( 'Withdrawal date', 'woocommerce-payments' );
}

const depositDateItem = (
return (
<SummaryItem
key="depositDate"
label={
Expand All @@ -142,16 +128,58 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( {
)
}
value={ <DepositStatusIndicator deposit={ deposit } /> }
detail={ deposit.bankAccount }
detail={
<>
{ deposit.bankAccount }
<br />
Bank reference ID:{ ' ' }
{ deposit.bank_reference_key ? (
<>
<span className="woopayments-payout-bank-reference-id">
{ deposit.bank_reference_key }
</span>
<CopyButton
textToCopy={ deposit.bank_reference_key }
label={ __(
'Copy bank reference ID to clipboard',
'woocommerce-payments'
) }
/>
</>
) : (
'N/A'
) }
</>
}
/>
);
};
interface DepositOverviewProps {
deposit: CachedDeposit | undefined;
}

export const DepositOverview: React.FC< DepositOverviewProps > = ( {
deposit,
} ) => {
if ( ! deposit ) {
return (
<InlineNotice icon status="error" isDismissible={ false }>
{ __(
`The deposit you are looking for cannot be found.`,
'woocommerce-payments'
) }
</InlineNotice>
);
}

const isWithdrawal = deposit.type === 'withdrawal';

return (
<div className="wcpay-deposit-overview">
{ deposit.automatic ? (
<Card className="wcpay-deposit-automatic">
<ul>
{ depositDateItem }
<DepositDateItem deposit={ deposit } />
<li className="wcpay-deposit-amount">
{ formatExplicitCurrency(
deposit.amount,
Expand All @@ -161,7 +189,7 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( {
</ul>
</Card>
) : (
<SummaryList
<SummaryList // For instant deposits only
label={
isWithdrawal
? __(
Expand All @@ -172,7 +200,7 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( {
}
>
{ () => [
depositDateItem,
<DepositDateItem key="dateItem" deposit={ deposit } />,
<SummaryItem
key="depositAmount"
label={
Expand Down
23 changes: 23 additions & 0 deletions client/deposits/details/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@
font-size: 20px;
line-height: 28px;
}

.wcpay-summary__item-detail {
color: $dark-gray-500;
word-wrap: break-word;

.woopayments-payout-bank-reference-id {
font-family: monospace;
}
}
}

.wcpay-deposit-fee {
Expand All @@ -52,13 +61,21 @@
margin: 0;
list-style-type: none;

@include breakpoint( '<660px' ) {
flex-direction: column;
}

.woocommerce-summary__item {
border-bottom: 0;
background-color: inherit;
color: inherit;
.woocommerce-summary__item-label:hover {
color: inherit;
}

@include breakpoint( '<660px' ) {
border-right: none;
}
}

.wcpay-deposit-amount {
Expand All @@ -67,6 +84,12 @@
text-align: right;
font-size: 36px;
line-height: 82px;

@include breakpoint( '<660px' ) {
line-height: 36px;
text-align: left;
border-top: 1px solid $studio-gray-5;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are we tweaking for smaller (<660px) screens? Should we make that behaviour the default, and progressively enhance on larger screens (i.e. mobile first)?

Copy link
Contributor Author

@nagpai nagpai Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are making the font size smaller, making it left aligned and adding a border on top.

The layout by itself - overall is desktop first. The columns have a sideways scroll. My gut feel is that merchants would mostly use a tablet or a laptop to check WP admin. Hence would prefer keeping it as-is, but open to revisiting that! The padding property within the media query is redundant though, removed it - 8bda02c.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are making the font size smaller, making it left aligned and adding a border on top.

Why? Ideally the style changes for smaller screens are minimal (so it's easy to maintain), this sounds like a lot! If you are adding different behaviour for different screen sizes I'd recommend including details in the PR description – I see you have a screenshot, but even better if you explain why the changes are needed.

Copy link
Contributor Author

@nagpai nagpai Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are adding different behaviour for different screen sizes I'd recommend including details in the PR description – I see you have a screenshot, but even better if you explain why the changes are needed.

I have added a note on why we are doing the CSS change, in the PR description

The PR also adds a few CSS fixes for mobile view. This is needed to change the alignment of the summary items as column, and adjust borders + text alignment for smaller screens < 660px .

}
}

Expand Down
Loading
Loading