Add variation inventory quick actions (#39935)
* Add new inventory submenu to variations actions * Move pricing submenu up to its own function * Add tests * Move inventory markup to seperate component * Remove menu group label * Add changelog * Move pricing menu to own component * Fix lint errors * Update use of handlePrompt to make return more flexible
This commit is contained in:
parent
1356f76f7b
commit
db9cb4db4b
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add new action menu item to variations list for managing inventory.
|
|
@ -0,0 +1 @@
|
|||
export * from './inventory-menu-item';
|
|
@ -0,0 +1,213 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { ProductVariation } from '@woocommerce/data';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
import { Dropdown, MenuGroup, MenuItem } from '@wordpress/components';
|
||||
import { createElement } from '@wordpress/element';
|
||||
import { chevronRight } from '@wordpress/icons';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { TRACKS_SOURCE } from '../../../constants';
|
||||
import { PRODUCT_STOCK_STATUS_KEYS } from '../../../utils/get-product-stock-status';
|
||||
|
||||
export type InventoryMenuItemProps = {
|
||||
variation: ProductVariation;
|
||||
handlePrompt(
|
||||
label?: string,
|
||||
parser?: ( value: string ) => Partial< ProductVariation > | null
|
||||
): void;
|
||||
onChange( values: Partial< ProductVariation > ): void;
|
||||
onClose(): void;
|
||||
};
|
||||
|
||||
export function InventoryMenuItem( {
|
||||
variation,
|
||||
handlePrompt,
|
||||
onChange,
|
||||
onClose,
|
||||
}: InventoryMenuItemProps ) {
|
||||
return (
|
||||
<Dropdown
|
||||
position="middle right"
|
||||
renderToggle={ ( { isOpen, onToggle } ) => (
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_inventory_click',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
onToggle();
|
||||
} }
|
||||
aria-expanded={ isOpen }
|
||||
icon={ chevronRight }
|
||||
iconPosition="right"
|
||||
>
|
||||
{ __( 'Inventory', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
) }
|
||||
renderContent={ () => (
|
||||
<div className="components-dropdown-menu__menu">
|
||||
<MenuGroup>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'stock_quantity_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt( undefined, ( value ) => {
|
||||
const stockQuantity = Number( value );
|
||||
if ( Number.isNaN( stockQuantity ) ) {
|
||||
return {};
|
||||
}
|
||||
recordEvent(
|
||||
'product_variations_menu_inventory_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'stock_quantity_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
stock_quantity: stockQuantity,
|
||||
manage_stock: true,
|
||||
};
|
||||
} );
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __( 'Update stock', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'manage_stock_toggle',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
onChange( {
|
||||
manage_stock: ! variation.manage_stock,
|
||||
} );
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __( 'Toggle "track quantity"', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'set_status_in_stock',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
onChange( {
|
||||
stock_status:
|
||||
PRODUCT_STOCK_STATUS_KEYS.instock,
|
||||
manage_stock: false,
|
||||
} );
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __( 'Set status to In stock', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'set_status_out_of_stock',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
onChange( {
|
||||
stock_status:
|
||||
PRODUCT_STOCK_STATUS_KEYS.outofstock,
|
||||
manage_stock: false,
|
||||
} );
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __(
|
||||
'Set status to Out of stock',
|
||||
'woocommerce'
|
||||
) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'set_status_on_back_order',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
onChange( {
|
||||
stock_status:
|
||||
PRODUCT_STOCK_STATUS_KEYS.onbackorder,
|
||||
manage_stock: false,
|
||||
} );
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __(
|
||||
'Set status to On back order',
|
||||
'woocommerce'
|
||||
) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'low_stock_amount_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt( undefined, ( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'low_stock_amount_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
const lowStockAmount = Number( value );
|
||||
if ( Number.isNaN( lowStockAmount ) ) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
low_stock_amount: lowStockAmount,
|
||||
manage_stock: true,
|
||||
};
|
||||
} );
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __( 'Edit low stock threshold', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
</MenuGroup>
|
||||
</div>
|
||||
) }
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from './pricing-menu-item';
|
|
@ -0,0 +1,357 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { ProductVariation } from '@woocommerce/data';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
import { Dropdown, MenuGroup, MenuItem } from '@wordpress/components';
|
||||
import { createElement } from '@wordpress/element';
|
||||
import { chevronRight } from '@wordpress/icons';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { TRACKS_SOURCE } from '../../../constants';
|
||||
|
||||
function isPercentage( value: string ) {
|
||||
return value.endsWith( '%' );
|
||||
}
|
||||
|
||||
function parsePercentage( value: string ) {
|
||||
const stringNumber = value.substring( 0, value.length - 1 );
|
||||
if ( Number.isNaN( Number( stringNumber ) ) ) {
|
||||
return undefined;
|
||||
}
|
||||
return Number( stringNumber );
|
||||
}
|
||||
|
||||
function addFixedOrPercentage(
|
||||
value: string,
|
||||
fixedOrPercentage: string,
|
||||
increaseOrDecrease: 1 | -1 = 1
|
||||
) {
|
||||
if ( isPercentage( fixedOrPercentage ) ) {
|
||||
if ( Number.isNaN( Number( value ) ) ) {
|
||||
return 0;
|
||||
}
|
||||
const percentage = parsePercentage( fixedOrPercentage );
|
||||
if ( percentage === undefined ) {
|
||||
return Number( value );
|
||||
}
|
||||
return (
|
||||
Number( value ) +
|
||||
Number( value ) * ( percentage / 100 ) * increaseOrDecrease
|
||||
);
|
||||
}
|
||||
if ( Number.isNaN( Number( value ) ) ) {
|
||||
if ( Number.isNaN( Number( fixedOrPercentage ) ) ) {
|
||||
return undefined;
|
||||
}
|
||||
return Number( fixedOrPercentage );
|
||||
}
|
||||
return Number( value ) + Number( fixedOrPercentage ) * increaseOrDecrease;
|
||||
}
|
||||
|
||||
export type PricingMenuItemProps = {
|
||||
variation: ProductVariation;
|
||||
handlePrompt(
|
||||
label?: string,
|
||||
parser?: ( value: string ) => Partial< ProductVariation >
|
||||
): void;
|
||||
onClose(): void;
|
||||
};
|
||||
|
||||
export function PricingMenuItem( {
|
||||
variation,
|
||||
handlePrompt,
|
||||
onClose,
|
||||
}: PricingMenuItemProps ) {
|
||||
return (
|
||||
<Dropdown
|
||||
position="middle right"
|
||||
renderToggle={ ( { isOpen, onToggle } ) => (
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent( 'product_variations_menu_pricing_click', {
|
||||
source: TRACKS_SOURCE,
|
||||
variation_id: variation.id,
|
||||
} );
|
||||
onToggle();
|
||||
} }
|
||||
aria-expanded={ isOpen }
|
||||
icon={ chevronRight }
|
||||
iconPosition="right"
|
||||
>
|
||||
{ __( 'Pricing', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
) }
|
||||
renderContent={ () => (
|
||||
<div className="components-dropdown-menu__menu">
|
||||
<MenuGroup label={ __( 'List price', 'woocommerce' ) }>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'list_price_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt( undefined, ( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'list_price_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
regular_price: value,
|
||||
};
|
||||
} );
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __( 'Set list price', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'list_price_increase',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
__(
|
||||
'Enter a value (fixed or %)',
|
||||
'woocommerce'
|
||||
),
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'list_price_increase',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
regular_price: addFixedOrPercentage(
|
||||
variation.regular_price,
|
||||
value
|
||||
)?.toFixed( 2 ),
|
||||
};
|
||||
}
|
||||
);
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __( 'Increase list price', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'list_price_decrease',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
__(
|
||||
'Enter a value (fixed or %)',
|
||||
'woocommerce'
|
||||
),
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'list_price_increase',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
regular_price: addFixedOrPercentage(
|
||||
variation.regular_price,
|
||||
value,
|
||||
-1
|
||||
)?.toFixed( 2 ),
|
||||
};
|
||||
}
|
||||
);
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __( 'Decrease list price', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
</MenuGroup>
|
||||
<MenuGroup label={ __( 'Sale price', 'woocommerce' ) }>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt( undefined, ( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
sale_price: value,
|
||||
};
|
||||
} );
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __( 'Set sale price', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_increase',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
__(
|
||||
'Enter a value (fixed or %)',
|
||||
'woocommerce'
|
||||
),
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_increase',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
sale_price: addFixedOrPercentage(
|
||||
variation.sale_price,
|
||||
value
|
||||
)?.toFixed( 2 ),
|
||||
};
|
||||
}
|
||||
);
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __( 'Increase sale price', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_decrease',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
__(
|
||||
'Enter a value (fixed or %)',
|
||||
'woocommerce'
|
||||
),
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_decrease',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
sale_price: addFixedOrPercentage(
|
||||
variation.sale_price,
|
||||
value,
|
||||
-1
|
||||
)?.toFixed( 2 ),
|
||||
};
|
||||
}
|
||||
);
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __( 'Decrease sale price', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_schedule',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
__(
|
||||
'Sale start date (YYYY-MM-DD format or leave blank)',
|
||||
'woocommerce'
|
||||
),
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_schedule',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
date_on_sale_from_gmt: value,
|
||||
};
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
__(
|
||||
'Sale end date (YYYY-MM-DD format or leave blank)',
|
||||
'woocommerce'
|
||||
),
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_schedule',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
date_on_sale_to_gmt: value,
|
||||
};
|
||||
}
|
||||
);
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __( 'Schedule sale', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
</MenuGroup>
|
||||
</div>
|
||||
) }
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -49,24 +49,22 @@ export function ShippingMenuItem( {
|
|||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
'dimensions',
|
||||
undefined,
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_shipping_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'dimensions_length_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
handlePrompt( undefined, ( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_shipping_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'dimensions_length_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
dimensions: {
|
||||
...variation.dimensions,
|
||||
length: value,
|
||||
};
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
} );
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
|
@ -82,24 +80,22 @@ export function ShippingMenuItem( {
|
|||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
'dimensions',
|
||||
undefined,
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_shipping_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'dimensions_width_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
handlePrompt( undefined, ( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_shipping_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'dimensions_width_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
dimensions: {
|
||||
...variation.dimensions,
|
||||
width: value,
|
||||
};
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
} );
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
|
@ -115,24 +111,22 @@ export function ShippingMenuItem( {
|
|||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
'dimensions',
|
||||
undefined,
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_shipping_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'dimensions_height_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
handlePrompt( undefined, ( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_shipping_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'dimensions_height_set',
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return {
|
||||
dimensions: {
|
||||
...variation.dimensions,
|
||||
height: value,
|
||||
};
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
} );
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
|
@ -148,7 +142,7 @@ export function ShippingMenuItem( {
|
|||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt( 'weight', undefined, ( value ) => {
|
||||
handlePrompt( undefined, ( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_shipping_update',
|
||||
{
|
||||
|
@ -157,7 +151,7 @@ export function ShippingMenuItem( {
|
|||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
return value;
|
||||
return { weight: value };
|
||||
} );
|
||||
onClose();
|
||||
} }
|
||||
|
|
|
@ -6,9 +6,8 @@ import { ProductVariation } from '@woocommerce/data';
|
|||
export type ShippingMenuItemProps = {
|
||||
variation: ProductVariation;
|
||||
handlePrompt(
|
||||
propertyName: keyof ProductVariation,
|
||||
label?: string,
|
||||
parser?: ( value: string ) => unknown
|
||||
parser?: ( value: string ) => Partial< ProductVariation > | null
|
||||
): void;
|
||||
onClose(): void;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,319 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { ProductVariation } from '@woocommerce/data';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
import React, { createElement } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { VariationActionsMenu } from '../';
|
||||
import { TRACKS_SOURCE } from '../../../../constants';
|
||||
import { PRODUCT_STOCK_STATUS_KEYS } from '../../../../utils/get-product-stock-status';
|
||||
|
||||
jest.mock( '@woocommerce/tracks', () => ( {
|
||||
recordEvent: jest.fn(),
|
||||
} ) );
|
||||
const mockVariation = {
|
||||
id: 10,
|
||||
manage_stock: false,
|
||||
attributes: [],
|
||||
} as ProductVariation;
|
||||
|
||||
describe( 'VariationActionsMenu', () => {
|
||||
let onChangeMock: jest.Mock, onDeleteMock: jest.Mock;
|
||||
beforeEach( () => {
|
||||
onChangeMock = jest.fn();
|
||||
onDeleteMock = jest.fn();
|
||||
( recordEvent as jest.Mock ).mockClear();
|
||||
} );
|
||||
|
||||
it( 'should trigger product_variations_menu_view track when dropdown toggled', () => {
|
||||
const { getByRole } = render(
|
||||
<VariationActionsMenu
|
||||
variation={ mockVariation }
|
||||
onChange={ onChangeMock }
|
||||
onDelete={ onDeleteMock }
|
||||
/>
|
||||
);
|
||||
fireEvent.click( getByRole( 'button', { name: 'Actions' } ) );
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'product_variations_menu_view',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
variation_id: 10,
|
||||
}
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should render dropdown with pricing, inventory, and delete options when opened', () => {
|
||||
const { queryByText, getByRole } = render(
|
||||
<VariationActionsMenu
|
||||
variation={ mockVariation }
|
||||
onChange={ onChangeMock }
|
||||
onDelete={ onDeleteMock }
|
||||
/>
|
||||
);
|
||||
fireEvent.click( getByRole( 'button', { name: 'Actions' } ) );
|
||||
expect( queryByText( 'Pricing' ) ).toBeInTheDocument();
|
||||
expect( queryByText( 'Inventory' ) ).toBeInTheDocument();
|
||||
expect( queryByText( 'Delete' ) ).toBeInTheDocument();
|
||||
} );
|
||||
|
||||
it( 'should call onDelete when Delete menuItem is clicked', async () => {
|
||||
const { getByRole, getByText } = render(
|
||||
<VariationActionsMenu
|
||||
variation={ mockVariation }
|
||||
onChange={ onChangeMock }
|
||||
onDelete={ onDeleteMock }
|
||||
/>
|
||||
);
|
||||
await fireEvent.click( getByRole( 'button', { name: 'Actions' } ) );
|
||||
await fireEvent.click( getByText( 'Delete' ) );
|
||||
expect( onDeleteMock ).toHaveBeenCalled();
|
||||
} );
|
||||
|
||||
describe( 'Inventory sub-menu', () => {
|
||||
it( 'should open Inventory sub-menu if Inventory is clicked with click track', async () => {
|
||||
const { queryByText, getByRole, getByText } = render(
|
||||
<VariationActionsMenu
|
||||
variation={ { ...mockVariation } }
|
||||
onChange={ onChangeMock }
|
||||
onDelete={ onDeleteMock }
|
||||
/>
|
||||
);
|
||||
await fireEvent.click( getByRole( 'button', { name: 'Actions' } ) );
|
||||
await fireEvent.click( getByText( 'Inventory' ) );
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'product_variations_menu_inventory_click',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
variation_id: 10,
|
||||
}
|
||||
);
|
||||
expect( queryByText( 'Update stock' ) ).toBeInTheDocument();
|
||||
expect(
|
||||
queryByText( 'Toggle "track quantity"' )
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
queryByText( 'Set status to In stock' )
|
||||
).toBeInTheDocument();
|
||||
} );
|
||||
|
||||
it( 'should onChange with stock_quantity when Update stock is clicked', async () => {
|
||||
window.prompt = jest.fn().mockReturnValue( '10' );
|
||||
const { getByRole, getByText } = render(
|
||||
<VariationActionsMenu
|
||||
variation={ { ...mockVariation } }
|
||||
onChange={ onChangeMock }
|
||||
onDelete={ onDeleteMock }
|
||||
/>
|
||||
);
|
||||
await fireEvent.click( getByRole( 'button', { name: 'Actions' } ) );
|
||||
await fireEvent.click( getByText( 'Inventory' ) );
|
||||
await fireEvent.click( getByText( 'Update stock' ) );
|
||||
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'stock_quantity_set',
|
||||
variation_id: 10,
|
||||
}
|
||||
);
|
||||
expect( onChangeMock ).toHaveBeenCalledWith( {
|
||||
stock_quantity: 10,
|
||||
manage_stock: true,
|
||||
} );
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'product_variations_menu_inventory_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'stock_quantity_set',
|
||||
variation_id: 10,
|
||||
}
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should not call onChange when prompt is cancelled', async () => {
|
||||
window.prompt = jest.fn().mockReturnValue( null );
|
||||
const { getByRole, getByText } = render(
|
||||
<VariationActionsMenu
|
||||
variation={ { ...mockVariation } }
|
||||
onChange={ onChangeMock }
|
||||
onDelete={ onDeleteMock }
|
||||
/>
|
||||
);
|
||||
await fireEvent.click( getByRole( 'button', { name: 'Actions' } ) );
|
||||
await fireEvent.click( getByText( 'Inventory' ) );
|
||||
await fireEvent.click( getByText( 'Update stock' ) );
|
||||
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'stock_quantity_set',
|
||||
variation_id: 10,
|
||||
}
|
||||
);
|
||||
expect( onChangeMock ).not.toHaveBeenCalledWith( {
|
||||
stock_quantity: 10,
|
||||
manage_stock: true,
|
||||
} );
|
||||
expect( recordEvent ).not.toHaveBeenCalledWith(
|
||||
'product_variations_menu_inventory_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'stock_quantity_set',
|
||||
variation_id: 10,
|
||||
}
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should call onChange with toggled manage_stock when toggle "track quantity" is clicked', async () => {
|
||||
const { getByRole, getByText, rerender } = render(
|
||||
<VariationActionsMenu
|
||||
variation={ { ...mockVariation } }
|
||||
onChange={ onChangeMock }
|
||||
onDelete={ onDeleteMock }
|
||||
/>
|
||||
);
|
||||
await fireEvent.click( getByRole( 'button', { name: 'Actions' } ) );
|
||||
await fireEvent.click( getByText( 'Inventory' ) );
|
||||
await fireEvent.click( getByText( 'Toggle "track quantity"' ) );
|
||||
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'manage_stock_toggle',
|
||||
variation_id: 10,
|
||||
}
|
||||
);
|
||||
expect( onChangeMock ).toHaveBeenCalledWith( {
|
||||
manage_stock: true,
|
||||
} );
|
||||
onChangeMock.mockClear();
|
||||
rerender(
|
||||
<VariationActionsMenu
|
||||
variation={ { ...mockVariation, manage_stock: true } }
|
||||
onChange={ onChangeMock }
|
||||
onDelete={ onDeleteMock }
|
||||
/>
|
||||
);
|
||||
await fireEvent.click( getByRole( 'button', { name: 'Actions' } ) );
|
||||
await fireEvent.click( getByText( 'Inventory' ) );
|
||||
await fireEvent.click( getByText( 'Toggle "track quantity"' ) );
|
||||
expect( onChangeMock ).toHaveBeenCalledWith( {
|
||||
manage_stock: false,
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'should call onChange with toggled stock_status when toggle "Set status to In stock" is clicked', async () => {
|
||||
const { getByRole, getByText } = render(
|
||||
<VariationActionsMenu
|
||||
variation={ { ...mockVariation } }
|
||||
onChange={ onChangeMock }
|
||||
onDelete={ onDeleteMock }
|
||||
/>
|
||||
);
|
||||
await fireEvent.click( getByRole( 'button', { name: 'Actions' } ) );
|
||||
await fireEvent.click( getByText( 'Inventory' ) );
|
||||
await fireEvent.click( getByText( 'Set status to In stock' ) );
|
||||
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'set_status_in_stock',
|
||||
variation_id: 10,
|
||||
}
|
||||
);
|
||||
expect( onChangeMock ).toHaveBeenCalledWith( {
|
||||
stock_status: PRODUCT_STOCK_STATUS_KEYS.instock,
|
||||
manage_stock: false,
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'should call onChange with toggled stock_status when toggle "Set status to Out of stock" is clicked', async () => {
|
||||
const { getByRole, getByText } = render(
|
||||
<VariationActionsMenu
|
||||
variation={ { ...mockVariation } }
|
||||
onChange={ onChangeMock }
|
||||
onDelete={ onDeleteMock }
|
||||
/>
|
||||
);
|
||||
await fireEvent.click( getByRole( 'button', { name: 'Actions' } ) );
|
||||
await fireEvent.click( getByText( 'Inventory' ) );
|
||||
await fireEvent.click( getByText( 'Set status to Out of stock' ) );
|
||||
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'set_status_out_of_stock',
|
||||
variation_id: 10,
|
||||
}
|
||||
);
|
||||
expect( onChangeMock ).toHaveBeenCalledWith( {
|
||||
stock_status: PRODUCT_STOCK_STATUS_KEYS.outofstock,
|
||||
manage_stock: false,
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'should call onChange with toggled stock_status when toggle "Set status to On back order" is clicked', async () => {
|
||||
const { getByRole, getByText } = render(
|
||||
<VariationActionsMenu
|
||||
variation={ { ...mockVariation } }
|
||||
onChange={ onChangeMock }
|
||||
onDelete={ onDeleteMock }
|
||||
/>
|
||||
);
|
||||
await fireEvent.click( getByRole( 'button', { name: 'Actions' } ) );
|
||||
await fireEvent.click( getByText( 'Inventory' ) );
|
||||
await fireEvent.click( getByText( 'Set status to On back order' ) );
|
||||
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'set_status_on_back_order',
|
||||
variation_id: 10,
|
||||
}
|
||||
);
|
||||
expect( onChangeMock ).toHaveBeenCalledWith( {
|
||||
stock_status: PRODUCT_STOCK_STATUS_KEYS.onbackorder,
|
||||
manage_stock: false,
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'should call onChange with low_stock_amount when Edit low stock threshold is clicked', async () => {
|
||||
window.prompt = jest.fn().mockReturnValue( '7' );
|
||||
const { getByRole, getByText } = render(
|
||||
<VariationActionsMenu
|
||||
variation={ { ...mockVariation } }
|
||||
onChange={ onChangeMock }
|
||||
onDelete={ onDeleteMock }
|
||||
/>
|
||||
);
|
||||
await fireEvent.click( getByRole( 'button', { name: 'Actions' } ) );
|
||||
await fireEvent.click( getByText( 'Inventory' ) );
|
||||
await fireEvent.click( getByText( 'Edit low stock threshold' ) );
|
||||
|
||||
expect( recordEvent ).toHaveBeenCalledWith(
|
||||
'product_variations_menu_inventory_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'low_stock_amount_set',
|
||||
variation_id: 10,
|
||||
}
|
||||
);
|
||||
expect( onChangeMock ).toHaveBeenCalledWith( {
|
||||
low_stock_amount: 7,
|
||||
manage_stock: true,
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -1,15 +1,10 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownMenu,
|
||||
MenuGroup,
|
||||
MenuItem,
|
||||
} from '@wordpress/components';
|
||||
import { DropdownMenu, MenuGroup, MenuItem } from '@wordpress/components';
|
||||
import { createElement, Fragment } from '@wordpress/element';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { chevronRight, moreVertical } from '@wordpress/icons';
|
||||
import { moreVertical } from '@wordpress/icons';
|
||||
import { ProductVariation } from '@woocommerce/data';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
|
||||
|
@ -19,45 +14,8 @@ import { recordEvent } from '@woocommerce/tracks';
|
|||
import { VariationActionsMenuProps } from './types';
|
||||
import { TRACKS_SOURCE } from '../../../constants';
|
||||
import { ShippingMenuItem } from '../shipping-menu-item';
|
||||
|
||||
function isPercentage( value: string ) {
|
||||
return value.endsWith( '%' );
|
||||
}
|
||||
|
||||
function parsePercentage( value: string ) {
|
||||
const stringNumber = value.substring( 0, value.length - 1 );
|
||||
if ( Number.isNaN( Number( stringNumber ) ) ) {
|
||||
return undefined;
|
||||
}
|
||||
return Number( stringNumber );
|
||||
}
|
||||
|
||||
function addFixedOrPercentage(
|
||||
value: string,
|
||||
fixedOrPercentage: string,
|
||||
increaseOrDecrease: 1 | -1 = 1
|
||||
) {
|
||||
if ( isPercentage( fixedOrPercentage ) ) {
|
||||
if ( Number.isNaN( Number( value ) ) ) {
|
||||
return 0;
|
||||
}
|
||||
const percentage = parsePercentage( fixedOrPercentage );
|
||||
if ( percentage === undefined ) {
|
||||
return Number( value );
|
||||
}
|
||||
return (
|
||||
Number( value ) +
|
||||
Number( value ) * ( percentage / 100 ) * increaseOrDecrease
|
||||
);
|
||||
}
|
||||
if ( Number.isNaN( Number( value ) ) ) {
|
||||
if ( Number.isNaN( Number( fixedOrPercentage ) ) ) {
|
||||
return undefined;
|
||||
}
|
||||
return Number( fixedOrPercentage );
|
||||
}
|
||||
return Number( value ) + Number( fixedOrPercentage ) * increaseOrDecrease;
|
||||
}
|
||||
import { InventoryMenuItem } from '../inventory-menu-item';
|
||||
import { PricingMenuItem } from '../pricing-menu-item';
|
||||
|
||||
export function VariationActionsMenu( {
|
||||
variation,
|
||||
|
@ -65,17 +23,18 @@ export function VariationActionsMenu( {
|
|||
onDelete,
|
||||
}: VariationActionsMenuProps ) {
|
||||
function handlePrompt(
|
||||
propertyName: keyof ProductVariation,
|
||||
label: string = __( 'Enter a value', 'woocommerce' ),
|
||||
parser: ( value: string ) => unknown = ( value ) => value
|
||||
parser: ( value: string ) => Partial< ProductVariation > | null = () =>
|
||||
null
|
||||
) {
|
||||
// eslint-disable-next-line no-alert
|
||||
const value = window.prompt( label );
|
||||
if ( value === null ) return;
|
||||
|
||||
onChange( {
|
||||
[ propertyName ]: parser( value.trim() ),
|
||||
} );
|
||||
const updates = parser( value.trim() );
|
||||
if ( updates ) {
|
||||
onChange( updates );
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -113,340 +72,17 @@ export function VariationActionsMenu( {
|
|||
</MenuItem>
|
||||
</MenuGroup>
|
||||
<MenuGroup>
|
||||
<Dropdown
|
||||
position="middle right"
|
||||
renderToggle={ ( { isOpen, onToggle } ) => (
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_click',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
variation_id: variation.id,
|
||||
}
|
||||
);
|
||||
onToggle();
|
||||
} }
|
||||
aria-expanded={ isOpen }
|
||||
icon={ chevronRight }
|
||||
iconPosition="right"
|
||||
>
|
||||
{ __( 'Pricing', 'woocommerce' ) }
|
||||
</MenuItem>
|
||||
) }
|
||||
renderContent={ () => (
|
||||
<div className="components-dropdown-menu__menu">
|
||||
<MenuGroup
|
||||
label={ __(
|
||||
'List price',
|
||||
'woocommerce'
|
||||
) }
|
||||
>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'list_price_set',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
'regular_price',
|
||||
undefined,
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'list_price_set',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
return value;
|
||||
}
|
||||
);
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __(
|
||||
'Set list price',
|
||||
'woocommerce'
|
||||
) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'list_price_increase',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
'regular_price',
|
||||
__(
|
||||
'Enter a value (fixed or %)',
|
||||
'woocommerce'
|
||||
),
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'list_price_increase',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
return addFixedOrPercentage(
|
||||
variation.regular_price,
|
||||
value
|
||||
)?.toFixed( 2 );
|
||||
}
|
||||
);
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __(
|
||||
'Increase list price',
|
||||
'woocommerce'
|
||||
) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'list_price_decrease',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
'regular_price',
|
||||
__(
|
||||
'Enter a value (fixed or %)',
|
||||
'woocommerce'
|
||||
),
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'list_price_increase',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
return addFixedOrPercentage(
|
||||
variation.regular_price,
|
||||
value,
|
||||
-1
|
||||
)?.toFixed( 2 );
|
||||
}
|
||||
);
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __(
|
||||
'Decrease list price',
|
||||
'woocommerce'
|
||||
) }
|
||||
</MenuItem>
|
||||
</MenuGroup>
|
||||
<MenuGroup
|
||||
label={ __(
|
||||
'Sale price',
|
||||
'woocommerce'
|
||||
) }
|
||||
>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_set',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
'sale_price',
|
||||
undefined,
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_set',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
return value;
|
||||
}
|
||||
);
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __(
|
||||
'Set sale price',
|
||||
'woocommerce'
|
||||
) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_increase',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
'sale_price',
|
||||
__(
|
||||
'Enter a value (fixed or %)',
|
||||
'woocommerce'
|
||||
),
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_increase',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
return addFixedOrPercentage(
|
||||
variation.sale_price,
|
||||
value
|
||||
)?.toFixed( 2 );
|
||||
}
|
||||
);
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __(
|
||||
'Increase sale price',
|
||||
'woocommerce'
|
||||
) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_decrease',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
'sale_price',
|
||||
__(
|
||||
'Enter a value (fixed or %)',
|
||||
'woocommerce'
|
||||
),
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_decrease',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
return addFixedOrPercentage(
|
||||
variation.sale_price,
|
||||
value,
|
||||
-1
|
||||
)?.toFixed( 2 );
|
||||
}
|
||||
);
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __(
|
||||
'Decrease sale price',
|
||||
'woocommerce'
|
||||
) }
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_select',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_schedule',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
'date_on_sale_from_gmt',
|
||||
__(
|
||||
'Sale start date (YYYY-MM-DD format or leave blank)',
|
||||
'woocommerce'
|
||||
),
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_schedule',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
return value;
|
||||
}
|
||||
);
|
||||
handlePrompt(
|
||||
'date_on_sale_to_gmt',
|
||||
__(
|
||||
'Sale end date (YYYY-MM-DD format or leave blank)',
|
||||
'woocommerce'
|
||||
),
|
||||
( value ) => {
|
||||
recordEvent(
|
||||
'product_variations_menu_pricing_update',
|
||||
{
|
||||
source: TRACKS_SOURCE,
|
||||
action: 'sale_price_schedule',
|
||||
variation_id:
|
||||
variation.id,
|
||||
}
|
||||
);
|
||||
return value;
|
||||
}
|
||||
);
|
||||
onClose();
|
||||
} }
|
||||
>
|
||||
{ __(
|
||||
'Schedule sale',
|
||||
'woocommerce'
|
||||
) }
|
||||
</MenuItem>
|
||||
</MenuGroup>
|
||||
</div>
|
||||
) }
|
||||
<PricingMenuItem
|
||||
variation={ variation }
|
||||
handlePrompt={ handlePrompt }
|
||||
onClose={ onClose }
|
||||
/>
|
||||
<InventoryMenuItem
|
||||
variation={ variation }
|
||||
handlePrompt={ handlePrompt }
|
||||
onChange={ onChange }
|
||||
onClose={ onClose }
|
||||
/>
|
||||
|
||||
<ShippingMenuItem
|
||||
variation={ variation }
|
||||
handlePrompt={ handlePrompt }
|
||||
|
@ -456,6 +92,7 @@ export function VariationActionsMenu( {
|
|||
<MenuGroup>
|
||||
<MenuItem
|
||||
isDestructive
|
||||
label={ __( 'Delete variation', 'woocommerce' ) }
|
||||
variant="link"
|
||||
onClick={ () => {
|
||||
onDelete( variation.id );
|
||||
|
|
Loading…
Reference in New Issue