Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
Merge branch 'trunk' into add/7722-add-client-side-postcode-validation
Browse files Browse the repository at this point in the history
  • Loading branch information
nielslange authored Mar 14, 2023
2 parents de8ee24 + 62cf7ab commit 3cc951b
Show file tree
Hide file tree
Showing 81 changed files with 1,877 additions and 343 deletions.
6 changes: 5 additions & 1 deletion assets/js/base/components/button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Spinner from '@woocommerce/base-components/spinner';
import './style.scss';

export interface ButtonProps
extends Omit< WPButtonType.ButtonProps, 'variant' > {
extends Omit< WPButtonType.ButtonProps, 'variant' | 'href' > {
/**
* Show spinner
*
Expand All @@ -23,6 +23,10 @@ export interface ButtonProps
* Button variant
*/
variant?: 'text' | 'contained' | 'outlined';
/**
* The URL the button should link to.
*/
href?: string | undefined;
}

export interface AnchorProps extends Omit< ButtonProps, 'href' > {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* External dependencies
*/
import { RadioControlOption } from '@woocommerce/base-components/radio-control/types';
import { CartShippingPackageShippingRate } from '@woocommerce/types';
/**
* Internal dependencies
*/
import RadioControl from '../../radio-control';

interface LocalPickupSelectProps {
title?: string | undefined;
setSelectedOption: ( value: string ) => void;
selectedOption: string;
pickupLocations: CartShippingPackageShippingRate[];
onSelectRate: ( value: string ) => void;
renderPickupLocation: (
location: CartShippingPackageShippingRate,
pickupLocationsCount: number
) => RadioControlOption;
packageCount: number;
}
/**
* Local pickup select component, used to render a package title and local pickup options.
*/
export const LocalPickupSelect = ( {
title,
setSelectedOption,
selectedOption,
pickupLocations,
onSelectRate,
renderPickupLocation,
packageCount,
}: LocalPickupSelectProps ) => {
// Hacky way to check if there are multiple packages, this way is borrowed from `assets/js/base/components/cart-checkout/shipping-rates-control-package/index.tsx`
// We have no built-in way of checking if other extensions have added packages.
const multiplePackages =
document.querySelectorAll(
'.wc-block-components-local-pickup-select .wc-block-components-radio-control'
).length > 1;
return (
<div className="wc-block-components-local-pickup-select">
{ multiplePackages && title ? <div>{ title }</div> : false }
<RadioControl
onChange={ ( value ) => {
setSelectedOption( value );
onSelectRate( value );
} }
selected={ selectedOption }
options={ pickupLocations.map( ( location ) =>
renderPickupLocation( location, packageCount )
) }
/>
</div>
);
};
export default LocalPickupSelect;
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
* External dependencies
*/
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
/**
* Internal dependencies
*/
import LocalPickupSelect from '..';

describe( 'LocalPickupSelect', () => {
const TestComponent = ( {
selectedOptionOverride = null,
onSelectRateOverride = null,
}: {
selectedOptionOverride?: null | ( ( value: string ) => void );
onSelectRateOverride?: null | ( ( value: string ) => void );
} ) => (
<LocalPickupSelect
title="Package 1"
setSelectedOption={ selectedOptionOverride || jest.fn() }
selectedOption=""
pickupLocations={ [
{
rate_id: '1',
currency_code: 'USD',
currency_decimal_separator: '.',
currency_minor_unit: 2,
currency_prefix: '$',
currency_suffix: '',
currency_thousand_separator: ',',
currency_symbol: '$',
name: 'Store 1',
description: 'Store 1 description',
delivery_time: '1 day',
price: '0',
taxes: '0',
instance_id: 1,
method_id: 'test_shipping:0',
meta_data: [],
selected: false,
},
{
rate_id: '2',
currency_code: 'USD',
currency_decimal_separator: '.',
currency_minor_unit: 2,
currency_prefix: '$',
currency_suffix: '',
currency_thousand_separator: ',',
currency_symbol: '$',
name: 'Store 2',
description: 'Store 2 description',
delivery_time: '2 days',
price: '0',
taxes: '0',
instance_id: 1,
method_id: 'test_shipping:1',
meta_data: [],
selected: false,
},
] }
onSelectRate={ onSelectRateOverride || jest.fn() }
packageCount={ 1 }
renderPickupLocation={ ( location ) => {
return {
value: `${ location.rate_id }`,
onChange: jest.fn(),
label: `${ location.name }`,
description: `${ location.description }`,
};
} }
/>
);
it( 'Does not render the title if only one package is present on the page', () => {
render( <TestComponent /> );
expect( screen.queryByText( 'Package 1' ) ).not.toBeInTheDocument();
} );
it( 'Does render the title if more than one package is present on the page', () => {
const { rerender } = render(
<div className="wc-block-components-local-pickup-select">
<div className="wc-block-components-radio-control"></div>
</div>
);
// Render twice so our component can check the DOM correctly.
rerender(
<>
<div className="wc-block-components-local-pickup-select">
<div className="wc-block-components-radio-control"></div>
</div>
<TestComponent />
</>
);
rerender(
<>
<div className="wc-block-components-local-pickup-select">
<div className="wc-block-components-radio-control"></div>
</div>
<TestComponent />
</>
);

expect( screen.getByText( 'Package 1' ) ).toBeInTheDocument();
} );
it( 'Calls the correct functions when changing selected option', () => {
const setSelectedOption = jest.fn();
const onSelectRate = jest.fn();
render(
<TestComponent
selectedOptionOverride={ setSelectedOption }
onSelectRateOverride={ onSelectRate }
/>
);
userEvent.click( screen.getByText( 'Store 2' ) );
expect( setSelectedOption ).toHaveBeenLastCalledWith( '2' );
expect( onSelectRate ).toHaveBeenLastCalledWith( '2' );
userEvent.click( screen.getByText( 'Store 1' ) );
expect( setSelectedOption ).toHaveBeenLastCalledWith( '1' );
expect( onSelectRate ).toHaveBeenLastCalledWith( '1' );
} );
} );
1 change: 1 addition & 0 deletions assets/js/base/components/state-input/StateInputProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface StateInputProps {
onChange: ( value: string ) => void;
required?: boolean;
errorMessage?: string | undefined;
errorId?: string;
}

export type StateInputWithStatesProps = StateInputProps & {
Expand Down
2 changes: 2 additions & 0 deletions assets/js/base/components/state-input/state-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const StateInput = ( {
autoComplete = 'off',
value = '',
required = false,
errorId = '',
}: StateInputWithStatesProps ): JSX.Element => {
const countryStates = states[ country ];
const options = useMemo(
Expand Down Expand Up @@ -102,6 +103,7 @@ const StateInput = ( {
'Please select a state.',
'woo-gutenberg-products-block'
) }
errorId={ errorId }
required={ required }
autoComplete={ autoComplete }
/>
Expand Down
2 changes: 2 additions & 0 deletions assets/js/base/context/hooks/cart/use-store-cart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
EMPTY_CART_ERRORS,
EMPTY_SHIPPING_RATES,
EMPTY_TAX_LINES,
EMPTY_PAYMENT_METHODS,
EMPTY_PAYMENT_REQUIREMENTS,
EMPTY_EXTENSIONS,
} from '@woocommerce/block-data';
Expand Down Expand Up @@ -112,6 +113,7 @@ export const defaultCartData: StoreCart = {
shippingRates: EMPTY_SHIPPING_RATES,
isLoadingRates: false,
cartHasCalculatedShipping: false,
paymentMethods: EMPTY_PAYMENT_METHODS,
paymentRequirements: EMPTY_PAYMENT_REQUIREMENTS,
receiveCart: () => undefined,
receiveCartContents: () => undefined,
Expand Down
3 changes: 2 additions & 1 deletion assets/js/base/context/hooks/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"../providers/cart-checkout/checkout-events/index.tsx",
"../providers/cart-checkout/payment-events/index.tsx",
"../providers/cart-checkout/shipping/index.js",
"../../../editor-components/utils/*"
"../../../editor-components/utils/*",
"../../../data/index.ts"
],
"exclude": [ "**/test/**" ]
}
4 changes: 4 additions & 0 deletions assets/js/base/utils/shipping-rates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
CartShippingRate,
} from '@woocommerce/type-defs/cart';
import { getSetting } from '@woocommerce/settings';
import { LOCAL_PICKUP_ENABLED } from '@woocommerce/block-settings';

/**
* Get the number of packages in a shippingRates array.
Expand Down Expand Up @@ -36,6 +37,9 @@ export const isPackageRateCollectable = (
export const hasCollectableRate = (
chosenRates: string[] | string
): boolean => {
if ( ! LOCAL_PICKUP_ENABLED ) {
return false;
}
if ( Array.isArray( chosenRates ) ) {
return !! chosenRates.find( ( rate ) =>
collectableMethodIds.includes( rate )
Expand Down
17 changes: 15 additions & 2 deletions assets/js/base/utils/test/shipping-rates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,40 @@ import {
isPackageRateCollectable,
} from '@woocommerce/base-utils';
import { CartShippingRate } from '@woocommerce/type-defs/cart';
import * as blockSettings from '@woocommerce/block-settings';

jest.mock( '@woocommerce/settings', () => {
return {
__esModule: true,
...jest.requireActual( '@woocommerce/settings' ),
getSetting: ( setting: string ) => {
getSetting: jest.fn().mockImplementation( ( setting: string ) => {
if ( setting === 'collectableMethodIds' ) {
return [ 'local_pickup' ];
}
return jest
.requireActual( '@woocommerce/settings' )
.getSetting( setting );
},
} ),
};
} );
jest.mock( '@woocommerce/block-settings', () => ( {
__esModule: true,
...jest.requireActual( '@woocommerce/block-settings' ),
LOCAL_PICKUP_ENABLED: true,
} ) );
describe( 'hasCollectableRate', () => {
it( 'correctly identifies if an array contains a collectable rate', () => {
const ratesToTest = [ 'flat_rate', 'local_pickup' ];
expect( hasCollectableRate( ratesToTest ) ).toBe( true );
const ratesToTest2 = [ 'flat_rate', 'free_shipping' ];
expect( hasCollectableRate( ratesToTest2 ) ).toBe( false );
} );
it( 'returns false for all rates if local pickup is disabled', () => {
// Attempt to assign to const or readonly variable error on next line is OK because it is mocked by jest
blockSettings.LOCAL_PICKUP_ENABLED = false;
const ratesToTest = [ 'flat_rate', 'local_pickup' ];
expect( hasCollectableRate( ratesToTest ) ).toBe( false );
} );
} );

describe( 'isPackageRateCollectable', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { dispatch } from '@wordpress/data';
* Internal dependencies
*/
import PaymentMethods from '../payment-methods';
import { defaultCartState } from '../../../../data/cart/default-state';

jest.mock( '../saved-payment-method-options', () => ( { onChange } ) => {
return (
Expand Down Expand Up @@ -102,9 +101,10 @@ describe( 'PaymentMethods', () => {
wpDataFunctions
.dispatch( CART_STORE_KEY )
.invalidateResolutionForStore();
wpDataFunctions
.dispatch( CART_STORE_KEY )
.receiveCart( defaultCartState.cartData );
wpDataFunctions.dispatch( CART_STORE_KEY ).receiveCart( {
...previewCart,
payment_methods: [ 'cod', 'credit-card' ],
} );
} );

afterEach( () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { CHECKOUT_URL } from '@woocommerce/block-settings';
import { usePositionRelativeToViewport } from '@woocommerce/base-hooks';
import { getSetting } from '@woocommerce/settings';
import { useSelect } from '@wordpress/data';
import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data';
import { CART_STORE_KEY, CHECKOUT_STORE_KEY } from '@woocommerce/block-data';
import { applyCheckoutFilter } from '@woocommerce/blocks-checkout';

/**
* Internal dependencies
Expand All @@ -28,10 +29,11 @@ const Block = ( {
className: string;
buttonLabel: string;
} ): JSX.Element => {
const link = getSetting( 'page-' + checkoutPageId, false );
const link = getSetting< string >( 'page-' + checkoutPageId, false );
const isCalculating = useSelect( ( select ) =>
select( CHECKOUT_STORE_KEY ).isCalculating()
);

const [ positionReferenceElement, positionRelativeToViewport ] =
usePositionRelativeToViewport();
const [ showSpinner, setShowSpinner ] = useState( false );
Expand All @@ -57,16 +59,30 @@ const Block = ( {
global.removeEventListener( 'pageshow', hideSpinner );
};
}, [] );
const cart = useSelect( ( select ) => {
return select( CART_STORE_KEY ).getCartData();
} );
const label = applyCheckoutFilter< string >( {
filterName: 'proceedToCheckoutButtonLabel',
defaultValue: buttonLabel || defaultButtonLabel,
arg: { cart },
} );

const filteredLink = applyCheckoutFilter< string >( {
filterName: 'proceedToCheckoutButtonLink',
defaultValue: link || CHECKOUT_URL,
arg: { cart },
} );

const submitContainerContents = (
<Button
className="wc-block-cart__submit-button"
href={ link || CHECKOUT_URL }
href={ filteredLink }
disabled={ isCalculating }
onClick={ () => setShowSpinner( true ) }
showSpinner={ showSpinner }
>
{ buttonLabel || defaultButtonLabel }
{ label }
</Button>
);

Expand Down
Loading

0 comments on commit 3cc951b

Please sign in to comment.