Merge branch 'trunk' into fix/load-theme-specific-stylesheet-in-site-editor
This commit is contained in:
commit
00b8a8e76c
|
@ -14,15 +14,11 @@
|
||||||
|
|
||||||
Closes # .
|
Closes # .
|
||||||
|
|
||||||
<!-- The next section is mandatory. If your PR doesn't require testing, please indicate that you are purposefully omitting instructions. -->
|
|
||||||
|
|
||||||
- [ ] This PR is a very minor change/addition and does not require testing instructions (if checked you can ignore/remove the next section).
|
|
||||||
|
|
||||||
<!-- Begin testing instructions -->
|
<!-- Begin testing instructions -->
|
||||||
|
|
||||||
### How to test the changes in this Pull Request:
|
### How to test the changes in this Pull Request:
|
||||||
|
|
||||||
<!-- Otherwise, please include detailed instructions on how these changes can be tested. Please, make sure to review and follow the guide for writing high-quality testing instructions below. -->
|
<!-- Please include detailed instructions on how these changes can be tested, make sure to review and follow the guide for writing high-quality testing instructions below. -->
|
||||||
|
|
||||||
- [ ] Have you followed the [Writing high-quality testing instructions guide](https://github.com/woocommerce/woocommerce/wiki/Writing-high-quality-testing-instructions)?
|
- [ ] Have you followed the [Writing high-quality testing instructions guide](https://github.com/woocommerce/woocommerce/wiki/Writing-high-quality-testing-instructions)?
|
||||||
|
|
||||||
|
@ -37,6 +33,7 @@ Closes # .
|
||||||
- [ ] Have you added an explanation of what your changes do and why you'd like us to include them?
|
- [ ] Have you added an explanation of what your changes do and why you'd like us to include them?
|
||||||
- [ ] Have you written new tests for your changes, as applicable?
|
- [ ] Have you written new tests for your changes, as applicable?
|
||||||
- [ ] Have you created a changelog file for each project being changed, ie `pnpm --filter=<project> changelog add`?
|
- [ ] Have you created a changelog file for each project being changed, ie `pnpm --filter=<project> changelog add`?
|
||||||
|
- [ ] Have you included testing instructions?
|
||||||
|
|
||||||
<!-- Mark completed items with an [x] -->
|
<!-- Mark completed items with an [x] -->
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,10 @@ name: 'Pull request post-merge processing'
|
||||||
on:
|
on:
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [closed]
|
types: [closed]
|
||||||
|
paths:
|
||||||
|
- 'packages/**'
|
||||||
|
- 'plugins/woocommerce/**'
|
||||||
|
- 'plugins/woocommerce-admin/**'
|
||||||
|
|
||||||
permissions: {}
|
permissions: {}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: update
|
||||||
|
|
||||||
|
Add deprecated message to product slot fill components
|
|
@ -26,17 +26,17 @@ A Slotfill component that will allow you to add a new field to a specific sectio
|
||||||
|
|
||||||
This is the fill component. You must provide the `id` prop to identify your product field fill with a unique string. This component will accept a series of props:
|
This is the fill component. You must provide the `id` prop to identify your product field fill with a unique string. This component will accept a series of props:
|
||||||
|
|
||||||
| Prop | Type | Description |
|
| Prop | Type | Description |
|
||||||
| -------------| -------- | ------------------------------------------------------------------------------------------------------------------------ |
|
| ---------- | ------ | ------------------------------------------------------------------------------------------------ |
|
||||||
| `id` | String | A unique string to identify your fill. Used for configuiration management. |
|
| `id` | String | A unique string to identify your fill. Used for configuration management. |
|
||||||
| `section ` | String | The string used to identify the particular section where you want to render your field. |
|
| `sections` | Array | Contains an array of name and order values for which slots it should be rendered in. |
|
||||||
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
||||||
| `order` | Number | (optional) This number will dictate the order that the fields rendered by a Slot will be appear. |
|
| `order` | Number | (optional) This number will dictate the order that the fields rendered by a Slot will be appear. |
|
||||||
|
|
||||||
### WooProductFieldItem.Slot (slot)
|
### WooProductFieldItem.Slot (slot)
|
||||||
|
|
||||||
This is the slot component, and will not be used as frequently. It must also receive the required `location` prop that will be identical to the fill `location`.
|
This is the slot component. This will render all the registered fills that match the `section` prop.
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ----------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
| --------- | ------ | --------------------------------------------------------------------------------------------------- |
|
||||||
| `section` | String | Unique to the section that the Slot appears, and must be the same as the one provided to any fills. |
|
| `section` | String | Unique to the section that the Slot appears, and must be the same as the one provided to any fills. |
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Slot, Fill } from '@wordpress/components';
|
import { Slot, Fill } from '@wordpress/components';
|
||||||
import { createElement, Children, Fragment } from '@wordpress/element';
|
import { createElement, Children, Fragment } from '@wordpress/element';
|
||||||
|
import deprecated from '@wordpress/deprecated';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -29,6 +30,8 @@ type WooProductFieldFillProps = {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_FIELD_ORDER = 20;
|
||||||
|
|
||||||
const WooProductFieldFill: React.FC< WooProductFieldFillProps > = ( {
|
const WooProductFieldFill: React.FC< WooProductFieldFillProps > = ( {
|
||||||
fieldName,
|
fieldName,
|
||||||
sectionName,
|
sectionName,
|
||||||
|
@ -39,6 +42,12 @@ const WooProductFieldFill: React.FC< WooProductFieldFillProps > = ( {
|
||||||
|
|
||||||
const fieldId = `product_field/${ sectionName }/${ fieldName }`;
|
const fieldId = `product_field/${ sectionName }/${ fieldName }`;
|
||||||
|
|
||||||
|
deprecated( `__experimentalWooProductFieldItem`, {
|
||||||
|
version: '13.0.0',
|
||||||
|
plugin: '@woocommerce/components',
|
||||||
|
hint: 'Moved to @woocommerce/product-editor package: import { __experimentalWooProductFieldItem } from @woocommerce/product-editor',
|
||||||
|
} );
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
registerFill( fieldId );
|
registerFill( fieldId );
|
||||||
}, [] );
|
}, [] );
|
||||||
|
@ -75,16 +84,18 @@ export const WooProductFieldItem: React.FC< WooProductFieldItemProps > & {
|
||||||
} = ( { children, sections, id } ) => {
|
} = ( { children, sections, id } ) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ sections.map( ( { name: sectionName, order = 20 } ) => (
|
{ sections.map(
|
||||||
<WooProductFieldFill
|
( { name: sectionName, order = DEFAULT_FIELD_ORDER } ) => (
|
||||||
fieldName={ id }
|
<WooProductFieldFill
|
||||||
sectionName={ sectionName }
|
fieldName={ id }
|
||||||
order={ order }
|
sectionName={ sectionName }
|
||||||
key={ sectionName }
|
order={ order }
|
||||||
>
|
key={ sectionName }
|
||||||
{ children }
|
>
|
||||||
</WooProductFieldFill>
|
{ children }
|
||||||
) ) }
|
</WooProductFieldFill>
|
||||||
|
)
|
||||||
|
) }
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -30,17 +30,17 @@ A Slotfill component that will allow you to add a new section to the product edi
|
||||||
|
|
||||||
This is the fill component. You must provide the `id` prop to identify your section fill with a unique string. This component will accept a series of props:
|
This is the fill component. You must provide the `id` prop to identify your section fill with a unique string. This component will accept a series of props:
|
||||||
|
|
||||||
| Prop | Type | Description |
|
| Prop | Type | Description |
|
||||||
| -------------| -------- | ------------------------------------------------------------------------------------------------------------------------ |
|
| ---------- | ------ | -------------------------------------------------------------------------------------------------- |
|
||||||
| `id` | String | A unique string to identify your fill. Used for configuiration management. |
|
| `id` | String | A unique string to identify your fill. Used for configuiration management. |
|
||||||
| `location` | String | The string used to identify the particular location that you want to render your section. |
|
| `tabs` | Array | Contains an array of name and order of which slots it should be rendered in. |
|
||||||
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
||||||
| `order` | Number | (optional) This number will dictate the order that the sections rendered by a Slot will be appear. |
|
| `order` | Number | (optional) This number will dictate the order that the sections rendered by a Slot will be appear. |
|
||||||
|
|
||||||
### WooProductSectionItem.Slot (slot)
|
### WooProductSectionItem.Slot (slot)
|
||||||
|
|
||||||
This is the slot component, and will not be used as frequently. It must also receive the required `location` prop that will be identical to the fill `location`.
|
This is the slot component. This will render all the registered fills that match the `tab` prop.
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ----------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
| ----- | ------ | ---------------------------------------------------------------------------------------------------- |
|
||||||
| `location` | String | Unique to the location that the Slot appears, and must be the same as the one provided to any fills. |
|
| `tab` | String | Unique to the location that the Slot appears, and must be the same as the one provided to any fills. |
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Slot, Fill } from '@wordpress/components';
|
import { Slot, Fill } from '@wordpress/components';
|
||||||
import { createElement, Fragment } from '@wordpress/element';
|
import { createElement, Fragment } from '@wordpress/element';
|
||||||
|
import deprecated from '@wordpress/deprecated';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -21,12 +22,20 @@ type WooProductSectionSlotProps = {
|
||||||
tab: string;
|
tab: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_SECTION_ORDER = 20;
|
||||||
|
|
||||||
export const WooProductSectionItem: React.FC< WooProductSectionItemProps > & {
|
export const WooProductSectionItem: React.FC< WooProductSectionItemProps > & {
|
||||||
Slot: React.FC< Slot.Props & WooProductSectionSlotProps >;
|
Slot: React.FC< Slot.Props & WooProductSectionSlotProps >;
|
||||||
} = ( { children, tabs } ) => {
|
} = ( { children, tabs } ) => {
|
||||||
|
deprecated( `__experimentalWooProductSectionItem`, {
|
||||||
|
version: '13.0.0',
|
||||||
|
plugin: '@woocommerce/components',
|
||||||
|
hint: 'Moved to @woocommerce/product-editor package: import { __experimentalWooProductSectionItem } from @woocommerce/product-editor',
|
||||||
|
} );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ tabs.map( ( { name: tabName, order: tabOrder } ) => (
|
{ tabs.map( ( { name: tabName, order: sectionOrder } ) => (
|
||||||
<Fill
|
<Fill
|
||||||
name={ `woocommerce_product_section_${ tabName }` }
|
name={ `woocommerce_product_section_${ tabName }` }
|
||||||
key={ tabName }
|
key={ tabName }
|
||||||
|
@ -34,7 +43,7 @@ export const WooProductSectionItem: React.FC< WooProductSectionItemProps > & {
|
||||||
{ ( fillProps: Fill.Props ) => {
|
{ ( fillProps: Fill.Props ) => {
|
||||||
return createOrderedChildren<
|
return createOrderedChildren<
|
||||||
Fill.Props & { tabName: string }
|
Fill.Props & { tabName: string }
|
||||||
>( children, tabOrder || 20, {
|
>( children, sectionOrder || DEFAULT_SECTION_ORDER, {
|
||||||
tabName,
|
tabName,
|
||||||
...fillProps,
|
...fillProps,
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -18,18 +18,18 @@ A Slotfill component that will allow you to add a new tab to the product editor.
|
||||||
|
|
||||||
This is the fill component. You must provide the `id` prop to identify your section fill with a unique string. This component will accept a series of props:
|
This is the fill component. You must provide the `id` prop to identify your section fill with a unique string. This component will accept a series of props:
|
||||||
|
|
||||||
| Prop | Type | Description |
|
| Prop | Type | Description |
|
||||||
| ---------- | ------ | -------------------------------------------------------------------------------------------------------------- |
|
| ----------- | ------ | -------------------------------------------------------------------------------------------------------------- |
|
||||||
| `id` | String | A unique string to identify your fill. Used for configuiration management. |
|
| `id` | String | A unique string to identify your fill. Used for configuiration management. |
|
||||||
| `location` | String | The string used to identify the particular location that you want to render your section. |
|
| `templates` | Array | Array of name and order of which template slots it should be rendered in |
|
||||||
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
||||||
| `tabProps` | Object | An object containing tab props: name, title, className, disabled (see TabPanel.Tab from @wordpress/components) |
|
| `tabProps` | Object | An object containing tab props: name, title, className, disabled (see TabPanel.Tab from @wordpress/components) |
|
||||||
| `order` | Number | (optional) This number will dictate the order that the sections rendered by a Slot will be appear. |
|
| `order` | Number | (optional) This number will dictate the order that the sections rendered by a Slot will be appear. |
|
||||||
|
|
||||||
### WooProductTabItem.Slot (slot)
|
### WooProductTabItem.Slot (slot)
|
||||||
|
|
||||||
This is the slot component, and will not be used as frequently. It must also receive the required `location` prop that will be identical to the fill `location`.
|
This is the slot component. This will render all the registered fills that match the `template` prop.
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ---------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
| ---------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
||||||
| `location` | String | Unique to the location that the Slot appears, and must be the same as the one provided to any fills. |
|
| `template` | String | Unique to the location that the Slot appears, and must be the same as the one provided to any fills. |
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import React, { ReactElement, ReactNode } from 'react';
|
import React, { ReactElement, ReactNode } from 'react';
|
||||||
import { Slot, Fill, TabPanel } from '@wordpress/components';
|
import { Slot, Fill, TabPanel } from '@wordpress/components';
|
||||||
import { createElement, Fragment } from '@wordpress/element';
|
import { createElement, Fragment } from '@wordpress/element';
|
||||||
|
import deprecated from '@wordpress/deprecated';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -30,11 +31,19 @@ type WooProductFieldSlotProps = {
|
||||||
) => ReactElement | null;
|
) => ReactElement | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_TAB_ORDER = 20;
|
||||||
|
|
||||||
export const WooProductTabItem: React.FC< WooProductTabItemProps > & {
|
export const WooProductTabItem: React.FC< WooProductTabItemProps > & {
|
||||||
Slot: React.VFC<
|
Slot: React.VFC<
|
||||||
Omit< Slot.Props, 'children' > & WooProductFieldSlotProps
|
Omit< Slot.Props, 'children' > & WooProductFieldSlotProps
|
||||||
>;
|
>;
|
||||||
} = ( { children, tabProps, templates } ) => {
|
} = ( { children, tabProps, templates } ) => {
|
||||||
|
deprecated( `__experimentalWooProductTabItem`, {
|
||||||
|
version: '13.0.0',
|
||||||
|
plugin: '@woocommerce/components',
|
||||||
|
hint: 'Moved to @woocommerce/product-editor package: import { __experimentalWooProductTabItem } from @woocommerce/product-editor',
|
||||||
|
} );
|
||||||
|
|
||||||
if ( ! templates ) {
|
if ( ! templates ) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.warn( 'WooProductTabItem fill is missing templates property.' );
|
console.warn( 'WooProductTabItem fill is missing templates property.' );
|
||||||
|
@ -50,12 +59,12 @@ export const WooProductTabItem: React.FC< WooProductTabItemProps > & {
|
||||||
{ ( fillProps: Fill.Props ) => {
|
{ ( fillProps: Fill.Props ) => {
|
||||||
return createOrderedChildren< Fill.Props >(
|
return createOrderedChildren< Fill.Props >(
|
||||||
children,
|
children,
|
||||||
templateData.order || 20,
|
templateData.order || DEFAULT_TAB_ORDER,
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
tabProps,
|
tabProps,
|
||||||
templateName: templateData.name,
|
templateName: templateData.name,
|
||||||
order: templateData.order || 20,
|
order: templateData.order || DEFAULT_TAB_ORDER,
|
||||||
...fillProps,
|
...fillProps,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -84,7 +93,7 @@ WooProductTabItem.Slot = ( { fillProps, template, children } ) => (
|
||||||
: props.tabProps;
|
: props.tabProps;
|
||||||
tabs.push( {
|
tabs.push( {
|
||||||
...tabProps,
|
...tabProps,
|
||||||
order: props.order ?? 20,
|
order: props.order ?? DEFAULT_TAB_ORDER,
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: add
|
||||||
|
|
||||||
|
Copy over the product slot fill components from @woocommerce/components
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: add
|
||||||
|
|
||||||
|
Add product editor utils
|
|
@ -29,19 +29,28 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@woocommerce/components": "workspace:*",
|
"@woocommerce/components": "workspace:*",
|
||||||
|
"@woocommerce/data": "workspace:^4.1.0",
|
||||||
|
"@woocommerce/tracks": "workspace:^1.3.0",
|
||||||
"@wordpress/components": "^19.5.0",
|
"@wordpress/components": "^19.5.0",
|
||||||
"@wordpress/element": "^4.1.1"
|
"@wordpress/element": "^4.1.1",
|
||||||
|
"@wordpress/i18n": "^4.26.0",
|
||||||
|
"react-router-dom": "^6.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/react": "^17.0.2",
|
||||||
"@types/wordpress__components": "^19.10.1",
|
"@types/wordpress__components": "^19.10.1",
|
||||||
"@woocommerce/eslint-plugin": "workspace:*",
|
"@woocommerce/eslint-plugin": "workspace:*",
|
||||||
"@woocommerce/internal-style-build": "workspace:*",
|
"@woocommerce/internal-style-build": "workspace:*",
|
||||||
"@wordpress/browserslist-config": "^4.1.1",
|
"@wordpress/browserslist-config": "^4.1.1",
|
||||||
|
"concurrently": "^7.0.0",
|
||||||
"css-loader": "^3.6.0",
|
"css-loader": "^3.6.0",
|
||||||
"eslint": "^8.32.0",
|
"eslint": "^8.32.0",
|
||||||
"jest": "^27.5.1",
|
"jest": "^27.5.1",
|
||||||
"jest-cli": "^27.5.1",
|
"jest-cli": "^27.5.1",
|
||||||
"postcss-loader": "^3.0.0",
|
"postcss-loader": "^3.0.0",
|
||||||
|
"react": "^17.0.2",
|
||||||
|
"react-dom": "^17.0.2",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
"sass-loader": "^10.2.1",
|
"sass-loader": "^10.2.1",
|
||||||
"ts-jest": "^27.1.3",
|
"ts-jest": "^27.1.3",
|
||||||
"typescript": "^4.8.3",
|
"typescript": "^4.8.3",
|
||||||
|
@ -60,5 +69,9 @@
|
||||||
"start": "concurrently \"tsc --build --watch\" \"webpack --watch\"",
|
"start": "concurrently \"tsc --build --watch\" \"webpack --watch\"",
|
||||||
"prepack": "pnpm run clean && pnpm run build",
|
"prepack": "pnpm run clean && pnpm run build",
|
||||||
"lint:fix": "eslint src --fix"
|
"lint:fix": "eslint src --fix"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^17.0.2",
|
||||||
|
"react": "^17.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export {
|
||||||
|
ProductSectionLayout as __experimentalProductSectionLayout,
|
||||||
|
ProductFieldSection as __experimentalProductFieldSection,
|
||||||
|
} from './product-section-layout';
|
||||||
|
export { WooProductFieldItem as __experimentalWooProductFieldItem } from './woo-product-field-item';
|
||||||
|
export { WooProductSectionItem as __experimentalWooProductSectionItem } from './woo-product-section-item';
|
||||||
|
export { WooProductTabItem as __experimentalWooProductTabItem } from './woo-product-tab-item';
|
|
@ -0,0 +1,42 @@
|
||||||
|
# WooProductFieldItem Slot & Fill
|
||||||
|
|
||||||
|
A Slotfill component that will allow you to add a new field to a specific section in the product editor.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
<WooProductFieldItem id={ key } section="details" order={ 2 } pluginId="test-plugin" >
|
||||||
|
{ () => {
|
||||||
|
return (
|
||||||
|
<TextControl
|
||||||
|
label="Name"
|
||||||
|
name={ `product-mvp-name` }
|
||||||
|
placeholder="e.g. 12 oz Coffee Mug"
|
||||||
|
value="Test Name"
|
||||||
|
onChange={ () => console.debug( 'Changed!' ) }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} }
|
||||||
|
</WooProductFieldItem>
|
||||||
|
|
||||||
|
<WooProductFieldItem.Slot section="details" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### WooProductFieldItem (fill)
|
||||||
|
|
||||||
|
This is the fill component. You must provide the `id` prop to identify your product field fill with a unique string. This component will accept a series of props:
|
||||||
|
|
||||||
|
| Prop | Type | Description |
|
||||||
|
| ---------- | ------ | ------------------------------------------------------------------------------------------------ |
|
||||||
|
| `id` | String | A unique string to identify your fill. Used for configuration management. |
|
||||||
|
| `sections` | Array | Contains an array of name and order values for which slots it should be rendered in. |
|
||||||
|
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
||||||
|
| `order` | Number | (optional) This number will dictate the order that the fields rendered by a Slot will be appear. |
|
||||||
|
|
||||||
|
### WooProductFieldItem.Slot (slot)
|
||||||
|
|
||||||
|
This is the slot component. This will render all the registered fills that match the `section` prop.
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| --------- | ------ | --------------------------------------------------------------------------------------------------- |
|
||||||
|
| `section` | String | Unique to the section that the Slot appears, and must be the same as the one provided to any fills. |
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './woo-product-field-item';
|
|
@ -0,0 +1,130 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { Slot, Fill } from '@wordpress/components';
|
||||||
|
import {
|
||||||
|
createElement,
|
||||||
|
Children,
|
||||||
|
Fragment,
|
||||||
|
useEffect,
|
||||||
|
} from '@wordpress/element';
|
||||||
|
import {
|
||||||
|
useSlotContext,
|
||||||
|
SlotContextHelpersType,
|
||||||
|
} from '@woocommerce/components';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { createOrderedChildren, sortFillsByOrder } from '../../utils';
|
||||||
|
import { ProductFillLocationType } from '../woo-product-tab-item';
|
||||||
|
|
||||||
|
type WooProductFieldItemProps = {
|
||||||
|
id: string;
|
||||||
|
sections: ProductFillLocationType[];
|
||||||
|
pluginId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type WooProductFieldSlotProps = {
|
||||||
|
section: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type WooProductFieldFillProps = {
|
||||||
|
fieldName: string;
|
||||||
|
sectionName: string;
|
||||||
|
order: number;
|
||||||
|
children?: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_FIELD_ORDER = 20;
|
||||||
|
|
||||||
|
const WooProductFieldFill: React.FC< WooProductFieldFillProps > = ( {
|
||||||
|
fieldName,
|
||||||
|
sectionName,
|
||||||
|
order,
|
||||||
|
children,
|
||||||
|
} ) => {
|
||||||
|
const { registerFill, getFillHelpers } = useSlotContext();
|
||||||
|
|
||||||
|
const fieldId = `product_field/${ sectionName }/${ fieldName }`;
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
registerFill( fieldId );
|
||||||
|
}, [] );
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fill
|
||||||
|
name={ `woocommerce_product_field_${ sectionName }` }
|
||||||
|
key={ fieldId }
|
||||||
|
>
|
||||||
|
{ ( fillProps: Fill.Props ) =>
|
||||||
|
createOrderedChildren<
|
||||||
|
Fill.Props &
|
||||||
|
SlotContextHelpersType & {
|
||||||
|
sectionName: string;
|
||||||
|
},
|
||||||
|
{ _id: string }
|
||||||
|
>(
|
||||||
|
children,
|
||||||
|
order,
|
||||||
|
{
|
||||||
|
sectionName,
|
||||||
|
...fillProps,
|
||||||
|
...getFillHelpers(),
|
||||||
|
},
|
||||||
|
{ _id: fieldId }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Fill>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WooProductFieldItem: React.FC< WooProductFieldItemProps > & {
|
||||||
|
Slot: React.FC< Slot.Props & WooProductFieldSlotProps >;
|
||||||
|
} = ( { children, sections, id } ) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{ sections.map(
|
||||||
|
( { name: sectionName, order = DEFAULT_FIELD_ORDER } ) => (
|
||||||
|
<WooProductFieldFill
|
||||||
|
fieldName={ id }
|
||||||
|
sectionName={ sectionName }
|
||||||
|
order={ order }
|
||||||
|
key={ sectionName }
|
||||||
|
>
|
||||||
|
{ children }
|
||||||
|
</WooProductFieldFill>
|
||||||
|
)
|
||||||
|
) }
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
WooProductFieldItem.Slot = ( { fillProps, section } ) => {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
const { filterRegisteredFills } = useSlotContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Slot
|
||||||
|
name={ `woocommerce_product_field_${ section }` }
|
||||||
|
fillProps={ fillProps }
|
||||||
|
>
|
||||||
|
{ ( fills ) => {
|
||||||
|
if ( ! sortFillsByOrder ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Children.map(
|
||||||
|
sortFillsByOrder( filterRegisteredFills( fills ) )?.props
|
||||||
|
.children,
|
||||||
|
( child ) => (
|
||||||
|
<div className="woocommerce-product-form__field">
|
||||||
|
{ child }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} }
|
||||||
|
</Slot>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,46 @@
|
||||||
|
# WooProductSectionItem Slot & Fill
|
||||||
|
|
||||||
|
A Slotfill component that will allow you to add a new section to the product editor.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
<WooProductSectionItem id={ key } location="tab/general" order={ 2 } pluginId="test-plugin" >
|
||||||
|
{ () => {
|
||||||
|
return (
|
||||||
|
<ProductSectionLayout
|
||||||
|
title={ __( 'Product test section', 'woocommerce' ) }
|
||||||
|
description={ __(
|
||||||
|
'In this area you can describe the section.',
|
||||||
|
'woocommerce'
|
||||||
|
) }
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<CardBody>{ /* Section content */ }</CardBody>
|
||||||
|
</Card>
|
||||||
|
</ProductSectionLayout>
|
||||||
|
);
|
||||||
|
} }
|
||||||
|
</WooProductSectionItem>
|
||||||
|
|
||||||
|
<WooProductSectionItem.Slot location="tab/general" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### WooProductSectionItem (fill)
|
||||||
|
|
||||||
|
This is the fill component. You must provide the `id` prop to identify your section fill with a unique string. This component will accept a series of props:
|
||||||
|
|
||||||
|
| Prop | Type | Description |
|
||||||
|
| ---------- | ------ | -------------------------------------------------------------------------------------------------- |
|
||||||
|
| `id` | String | A unique string to identify your fill. Used for configuration management. |
|
||||||
|
| `tabs` | Array | Contains an array of name and order of which slots it should be rendered in. |
|
||||||
|
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
||||||
|
| `order` | Number | (optional) This number will dictate the order that the sections rendered by a Slot will be appear. |
|
||||||
|
|
||||||
|
### WooProductSectionItem.Slot (slot)
|
||||||
|
|
||||||
|
This is the slot component. This will render all the registered fills that match the `tab` prop.
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| ----- | ------ | ---------------------------------------------------------------------------------------------------- |
|
||||||
|
| `tab` | String | Unique to the location that the Slot appears, and must be the same as the one provided to any fills. |
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './woo-product-section-item';
|
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import { Slot, Fill } from '@wordpress/components';
|
||||||
|
import { createElement, Fragment } from '@wordpress/element';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { createOrderedChildren, sortFillsByOrder } from '../../utils';
|
||||||
|
import { ProductFillLocationType } from '../woo-product-tab-item';
|
||||||
|
|
||||||
|
type WooProductSectionItemProps = {
|
||||||
|
id: string;
|
||||||
|
tabs: ProductFillLocationType[];
|
||||||
|
pluginId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type WooProductSectionSlotProps = {
|
||||||
|
tab: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_SECTION_ORDER = 20;
|
||||||
|
|
||||||
|
export const WooProductSectionItem: React.FC< WooProductSectionItemProps > & {
|
||||||
|
Slot: React.FC< Slot.Props & WooProductSectionSlotProps >;
|
||||||
|
} = ( { children, tabs } ) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{ tabs.map( ( { name: tabName, order: sectionOrder } ) => (
|
||||||
|
<Fill
|
||||||
|
name={ `woocommerce_product_section_${ tabName }` }
|
||||||
|
key={ tabName }
|
||||||
|
>
|
||||||
|
{ ( fillProps: Fill.Props ) => {
|
||||||
|
return createOrderedChildren<
|
||||||
|
Fill.Props & { tabName: string }
|
||||||
|
>( children, sectionOrder || DEFAULT_SECTION_ORDER, {
|
||||||
|
tabName,
|
||||||
|
...fillProps,
|
||||||
|
} );
|
||||||
|
} }
|
||||||
|
</Fill>
|
||||||
|
) ) }
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
WooProductSectionItem.Slot = ( { fillProps, tab } ) => (
|
||||||
|
<Slot
|
||||||
|
name={ `woocommerce_product_section_${ tab }` }
|
||||||
|
fillProps={ fillProps }
|
||||||
|
>
|
||||||
|
{ ( fills ) => {
|
||||||
|
if ( ! sortFillsByOrder ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sortFillsByOrder( fills );
|
||||||
|
} }
|
||||||
|
</Slot>
|
||||||
|
);
|
|
@ -0,0 +1,35 @@
|
||||||
|
# WooProductTabItem Slot & Fill
|
||||||
|
|
||||||
|
A Slotfill component that will allow you to add a new tab to the product editor.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
<WooProductTabItem id={ key } location="tab/general" order={ 2 } pluginId="test-plugin" tabProps={ { title: 'New tab', name: 'new-tab' } } >
|
||||||
|
<Card>
|
||||||
|
<CardBody>{ /* Tab content */ }</CardBody>
|
||||||
|
</Card>
|
||||||
|
</WooProductTabItem>
|
||||||
|
|
||||||
|
<WooProductTabItem.Slot location="tab/general" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### WooProductTabItem (fill)
|
||||||
|
|
||||||
|
This is the fill component. You must provide the `id` prop to identify your section fill with a unique string. This component will accept a series of props:
|
||||||
|
|
||||||
|
| Prop | Type | Description |
|
||||||
|
| ----------- | ------ | -------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `id` | String | A unique string to identify your fill. Used for configuration management. |
|
||||||
|
| `templates` | Array | Array of name and order of which template slots it should be rendered in |
|
||||||
|
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
||||||
|
| `tabProps` | Object | An object containing tab props: name, title, className, disabled (see TabPanel.Tab from @wordpress/components) |
|
||||||
|
| `order` | Number | (optional) This number will dictate the order that the sections rendered by a Slot will be appear. |
|
||||||
|
|
||||||
|
### WooProductTabItem.Slot (slot)
|
||||||
|
|
||||||
|
This is the slot component. This will render all the registered fills that match the `template` prop.
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| ---------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
||||||
|
| `template` | String | Unique to the location that the Slot appears, and must be the same as the one provided to any fills. |
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './woo-product-tab-item';
|
|
@ -0,0 +1,109 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import React, { ReactElement, ReactNode } from 'react';
|
||||||
|
import { Slot, Fill, TabPanel } from '@wordpress/components';
|
||||||
|
import { createElement, Fragment } from '@wordpress/element';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { createOrderedChildren } from '../../utils';
|
||||||
|
|
||||||
|
export type ProductFillLocationType = { name: string; order?: number };
|
||||||
|
|
||||||
|
type WooProductTabItemProps = {
|
||||||
|
id: string;
|
||||||
|
pluginId: string;
|
||||||
|
tabProps:
|
||||||
|
| TabPanel.Tab
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
| ( ( fillProps: Record< string, any > | undefined ) => TabPanel.Tab );
|
||||||
|
templates?: Array< ProductFillLocationType >;
|
||||||
|
};
|
||||||
|
|
||||||
|
type WooProductFieldSlotProps = {
|
||||||
|
template: string;
|
||||||
|
children: (
|
||||||
|
tabs: TabPanel.Tab[],
|
||||||
|
tabChildren: Record< string, ReactNode >
|
||||||
|
) => ReactElement | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_TAB_ORDER = 20;
|
||||||
|
|
||||||
|
export const WooProductTabItem: React.FC< WooProductTabItemProps > & {
|
||||||
|
Slot: React.VFC<
|
||||||
|
Omit< Slot.Props, 'children' > & WooProductFieldSlotProps
|
||||||
|
>;
|
||||||
|
} = ( { children, tabProps, templates } ) => {
|
||||||
|
if ( ! templates ) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn( 'WooProductTabItem fill is missing templates property.' );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{ templates.map( ( templateData ) => (
|
||||||
|
<Fill
|
||||||
|
name={ `woocommerce_product_tab_${ templateData.name }` }
|
||||||
|
key={ templateData.name }
|
||||||
|
>
|
||||||
|
{ ( fillProps: Fill.Props ) => {
|
||||||
|
return createOrderedChildren< Fill.Props >(
|
||||||
|
children,
|
||||||
|
templateData.order || DEFAULT_TAB_ORDER,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
tabProps,
|
||||||
|
templateName: templateData.name,
|
||||||
|
order: templateData.order || DEFAULT_TAB_ORDER,
|
||||||
|
...fillProps,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} }
|
||||||
|
</Fill>
|
||||||
|
) ) }
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
WooProductTabItem.Slot = ( { fillProps, template, children } ) => (
|
||||||
|
<Slot
|
||||||
|
name={ `woocommerce_product_tab_${ template }` }
|
||||||
|
fillProps={ fillProps }
|
||||||
|
>
|
||||||
|
{ ( fills ) => {
|
||||||
|
const tabData = fills.reduce(
|
||||||
|
( { childrenMap, tabs }, fill ) => {
|
||||||
|
const props: WooProductTabItemProps & { order: number } =
|
||||||
|
fill[ 0 ].props;
|
||||||
|
if ( props && props.tabProps ) {
|
||||||
|
childrenMap[ props.tabProps.name ] = fill[ 0 ];
|
||||||
|
const tabProps =
|
||||||
|
typeof props.tabProps === 'function'
|
||||||
|
? props.tabProps( fillProps )
|
||||||
|
: props.tabProps;
|
||||||
|
tabs.push( {
|
||||||
|
...tabProps,
|
||||||
|
order: props.order ?? DEFAULT_TAB_ORDER,
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
childrenMap,
|
||||||
|
tabs,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ childrenMap: {}, tabs: [] } as {
|
||||||
|
childrenMap: Record< string, ReactElement >;
|
||||||
|
tabs: Array< TabPanel.Tab & { order: number } >;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const orderedTabs = tabData.tabs.sort( ( a, b ) => {
|
||||||
|
return a.order - b.order;
|
||||||
|
} );
|
||||||
|
|
||||||
|
return children( orderedTabs, tabData.childrenMap );
|
||||||
|
} }
|
||||||
|
</Slot>
|
||||||
|
);
|
|
@ -1,4 +1,6 @@
|
||||||
export {
|
export * from './components';
|
||||||
ProductSectionLayout as __experimentalProductSectionLayout,
|
|
||||||
ProductFieldSection as __experimentalProductFieldSection,
|
/**
|
||||||
} from './product-section-layout';
|
* Utils
|
||||||
|
*/
|
||||||
|
export * from './utils';
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
@import 'product-section-layout/style.scss';
|
@import 'components/product-section-layout/style.scss';
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
export const NUMBERS_AND_ALLOWED_CHARS = '[^-0-9%s1%s2]';
|
||||||
|
export const NUMBERS_AND_DECIMAL_SEPARATOR = '[^-\\d\\%s]+';
|
||||||
|
export const ONLY_ONE_DECIMAL_SEPARATOR = '[%s](?=%s*[%s])';
|
||||||
|
// This should never be a real slug value of any existing shipping class
|
||||||
|
export const ADD_NEW_SHIPPING_CLASS_OPTION_VALUE =
|
||||||
|
'__ADD_NEW_SHIPPING_CLASS_OPTION__';
|
||||||
|
export const UNCATEGORIZED_CATEGORY_SLUG = 'uncategorized';
|
||||||
|
export const PRODUCT_VARIATION_TITLE_LIMIT = 32;
|
||||||
|
export const STANDARD_RATE_TAX_CLASS_SLUG = 'standard';
|
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { isValidElement, Fragment } from 'react';
|
||||||
|
import { Slot, Fill } from '@wordpress/components';
|
||||||
|
import { cloneElement, createElement } from '@wordpress/element';
|
||||||
|
|
||||||
|
type ChildrenProps = {
|
||||||
|
order: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object with the children and props that will be used by `cloneElement`. They will change depending on the
|
||||||
|
* type of children passed in.
|
||||||
|
*
|
||||||
|
* @param {Node} children - Node children.
|
||||||
|
* @param {number} order - Node order.
|
||||||
|
* @param {Array} props - Fill props.
|
||||||
|
* @param {Object} injectProps - Props to inject.
|
||||||
|
* @return {Object} Object with the keys: children and props.
|
||||||
|
*/
|
||||||
|
function getChildrenAndProps< T = Fill.Props, S = Record< string, unknown > >(
|
||||||
|
children: React.ReactNode,
|
||||||
|
order: number,
|
||||||
|
props: T,
|
||||||
|
injectProps?: S
|
||||||
|
) {
|
||||||
|
if ( typeof children === 'function' ) {
|
||||||
|
return {
|
||||||
|
children: children( { ...props, order, ...injectProps } ),
|
||||||
|
props: { order, ...injectProps },
|
||||||
|
};
|
||||||
|
} else if ( isValidElement( children ) ) {
|
||||||
|
// This checks whether 'children' is a react element or a standard HTML element.
|
||||||
|
if ( typeof children?.type === 'function' ) {
|
||||||
|
return {
|
||||||
|
children,
|
||||||
|
props: {
|
||||||
|
...props,
|
||||||
|
order,
|
||||||
|
...injectProps,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
children: children as React.ReactElement< ChildrenProps >,
|
||||||
|
props: { order, ...injectProps },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw Error( 'Invalid children type' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ordered fill item.
|
||||||
|
*
|
||||||
|
* @param {Node} children - Node children.
|
||||||
|
* @param {number} order - Node order.
|
||||||
|
* @param {Array} props - Fill props.
|
||||||
|
* @param {Object} injectProps - Props to inject.
|
||||||
|
* @return {Node} Node.
|
||||||
|
*/
|
||||||
|
function createOrderedChildren< T = Fill.Props, S = Record< string, unknown > >(
|
||||||
|
children: React.ReactNode,
|
||||||
|
order: number,
|
||||||
|
props: T,
|
||||||
|
injectProps?: S
|
||||||
|
) {
|
||||||
|
const { children: childrenToRender, props: propsToRender } =
|
||||||
|
getChildrenAndProps( children, order, props, injectProps );
|
||||||
|
return cloneElement( childrenToRender, propsToRender );
|
||||||
|
}
|
||||||
|
export { createOrderedChildren };
|
|
@ -0,0 +1,39 @@
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { NUMBERS_AND_ALLOWED_CHARS } from './constants';
|
||||||
|
|
||||||
|
type CurrencyConfig = {
|
||||||
|
code: string;
|
||||||
|
symbol: string;
|
||||||
|
symbolPosition: string;
|
||||||
|
decimalSeparator: string;
|
||||||
|
priceFormat: string;
|
||||||
|
thousandSeparator: string;
|
||||||
|
precision: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans and formats the currency value shown to the user.
|
||||||
|
*
|
||||||
|
* @param {string} value Form value.
|
||||||
|
* @param {Object} currencyConfig Currency context.
|
||||||
|
* @return {string} Display value.
|
||||||
|
*/
|
||||||
|
export const formatCurrencyDisplayValue = (
|
||||||
|
value: string,
|
||||||
|
currencyConfig: CurrencyConfig,
|
||||||
|
format: ( number: number | string ) => string
|
||||||
|
) => {
|
||||||
|
const { decimalSeparator, thousandSeparator } = currencyConfig;
|
||||||
|
|
||||||
|
const regex = new RegExp(
|
||||||
|
NUMBERS_AND_ALLOWED_CHARS.replace( '%s1', decimalSeparator ).replace(
|
||||||
|
'%s2',
|
||||||
|
thousandSeparator
|
||||||
|
),
|
||||||
|
'g'
|
||||||
|
);
|
||||||
|
|
||||||
|
return value === undefined ? value : format( value ).replace( regex, '' );
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { ChangeEvent } from 'react';
|
||||||
|
import { Product } from '@woocommerce/data';
|
||||||
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get additional props to be passed to all checkbox inputs.
|
||||||
|
*
|
||||||
|
* @param name Name of the checkbox.
|
||||||
|
* @return Props.
|
||||||
|
*/
|
||||||
|
export function getCheckboxTracks< T = Product >( name: string ) {
|
||||||
|
return {
|
||||||
|
onChange: (
|
||||||
|
isChecked: ChangeEvent< HTMLInputElement > | T[ keyof T ]
|
||||||
|
) => {
|
||||||
|
recordEvent( `product_checkbox_${ name }`, {
|
||||||
|
checked: isChecked,
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
type CurrencyConfig = {
|
||||||
|
code: string;
|
||||||
|
symbol: string;
|
||||||
|
symbolPosition: string;
|
||||||
|
decimalSeparator: string;
|
||||||
|
priceFormat: string;
|
||||||
|
thousandSeparator: string;
|
||||||
|
precision: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get input props for currency related values and symbol positions.
|
||||||
|
*
|
||||||
|
* @param {Object} currencyConfig - Currency context
|
||||||
|
* @return {Object} Props.
|
||||||
|
*/
|
||||||
|
export const getCurrencySymbolProps = ( currencyConfig: CurrencyConfig ) => {
|
||||||
|
const { symbol, symbolPosition } = currencyConfig;
|
||||||
|
const currencyPosition = symbolPosition.includes( 'left' )
|
||||||
|
? 'prefix'
|
||||||
|
: 'suffix';
|
||||||
|
|
||||||
|
return {
|
||||||
|
[ currencyPosition ]: symbol,
|
||||||
|
};
|
||||||
|
};
|
|
@ -6,7 +6,7 @@ import { ProductVariation } from '@woocommerce/data';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { PRODUCT_VARIATION_TITLE_LIMIT } from '../constants';
|
import { PRODUCT_VARIATION_TITLE_LIMIT } from './constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the product variation title for use in the header.
|
* Get the product variation title for use in the header.
|
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { formatCurrencyDisplayValue } from './format-currency-display-value';
|
||||||
|
import { getCheckboxTracks } from './get-checkbox-tracks';
|
||||||
|
import { getCurrencySymbolProps } from './get-currency-symbol-props';
|
||||||
|
import { getDerivedProductType } from './get-derived-product-type';
|
||||||
|
import { getProductStatus, PRODUCT_STATUS_LABELS } from './get-product-status';
|
||||||
|
import {
|
||||||
|
getProductStockStatus,
|
||||||
|
getProductStockStatusClass,
|
||||||
|
} from './get-product-stock-status';
|
||||||
|
import { getProductTitle, AUTO_DRAFT_NAME } from './get-product-title';
|
||||||
|
import {
|
||||||
|
getProductVariationTitle,
|
||||||
|
getTruncatedProductVariationTitle,
|
||||||
|
} from './get-product-variation-title';
|
||||||
|
import { preventLeavingProductForm } from './prevent-leaving-product-form';
|
||||||
|
|
||||||
|
export * from './create-ordered-children';
|
||||||
|
export * from './sort-fills-by-order';
|
||||||
|
|
||||||
|
export {
|
||||||
|
AUTO_DRAFT_NAME,
|
||||||
|
formatCurrencyDisplayValue,
|
||||||
|
getCheckboxTracks,
|
||||||
|
getCurrencySymbolProps,
|
||||||
|
getDerivedProductType,
|
||||||
|
getProductStatus,
|
||||||
|
getProductStockStatus,
|
||||||
|
getProductStockStatusClass,
|
||||||
|
getProductTitle,
|
||||||
|
getProductVariationTitle,
|
||||||
|
getTruncatedProductVariationTitle,
|
||||||
|
preventLeavingProductForm,
|
||||||
|
PRODUCT_STATUS_LABELS,
|
||||||
|
};
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { Fragment } from 'react';
|
||||||
|
import { Slot } from '@wordpress/components';
|
||||||
|
import { createElement } from '@wordpress/element';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort fills by order for slot children.
|
||||||
|
*
|
||||||
|
* @param {Array} fills - slot's `Fill`s.
|
||||||
|
* @return {Node} Node.
|
||||||
|
*/
|
||||||
|
export const sortFillsByOrder: Slot.Props[ 'children' ] = ( fills ) => {
|
||||||
|
// Copy fills array here because its type is readonly array that doesn't have .sort method in Typescript definition.
|
||||||
|
const sortedFills = [ ...fills ].sort( ( a, b ) => {
|
||||||
|
return a[ 0 ].props.order - b[ 0 ].props.order;
|
||||||
|
} );
|
||||||
|
|
||||||
|
return <Fragment>{ sortedFills }</Fragment>;
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
wcAdminFeatures: Record< string, boolean >;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*~ If your module exports nothing, you'll need this line. Otherwise, delete it */
|
||||||
|
export {};
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*/
|
*/
|
||||||
import { Component, Fragment } from '@wordpress/element';
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -13,6 +14,7 @@ import OrdersReportTable from './table';
|
||||||
import ReportChart from '../../components/report-chart';
|
import ReportChart from '../../components/report-chart';
|
||||||
import ReportSummary from '../../components/report-summary';
|
import ReportSummary from '../../components/report-summary';
|
||||||
import ReportFilters from '../../components/report-filters';
|
import ReportFilters from '../../components/report-filters';
|
||||||
|
import { ReportDateTour } from '~/guided-tours/report-date-tour';
|
||||||
|
|
||||||
export default class OrdersReport extends Component {
|
export default class OrdersReport extends Component {
|
||||||
render() {
|
render() {
|
||||||
|
@ -49,6 +51,13 @@ export default class OrdersReport extends Component {
|
||||||
filters={ filters }
|
filters={ filters }
|
||||||
advancedFilters={ advancedFilters }
|
advancedFilters={ advancedFilters }
|
||||||
/>
|
/>
|
||||||
|
<ReportDateTour
|
||||||
|
optionName="woocommerce_orders_report_date_tour_shown"
|
||||||
|
headingText={ __(
|
||||||
|
'Orders are now reported based on the payment dates ✅',
|
||||||
|
'woocommerce'
|
||||||
|
) }
|
||||||
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*/
|
*/
|
||||||
import { Component, Fragment } from '@wordpress/element';
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -13,6 +14,7 @@ import ReportChart from '../../components/report-chart';
|
||||||
import ReportSummary from '../../components/report-summary';
|
import ReportSummary from '../../components/report-summary';
|
||||||
import RevenueReportTable from './table';
|
import RevenueReportTable from './table';
|
||||||
import ReportFilters from '../../components/report-filters';
|
import ReportFilters from '../../components/report-filters';
|
||||||
|
import { ReportDateTour } from '~/guided-tours/report-date-tour';
|
||||||
|
|
||||||
export default class RevenueReport extends Component {
|
export default class RevenueReport extends Component {
|
||||||
render() {
|
render() {
|
||||||
|
@ -49,6 +51,13 @@ export default class RevenueReport extends Component {
|
||||||
filters={ filters }
|
filters={ filters }
|
||||||
advancedFilters={ advancedFilters }
|
advancedFilters={ advancedFilters }
|
||||||
/>
|
/>
|
||||||
|
<ReportDateTour
|
||||||
|
optionName="woocommerce_revenue_report_date_tour_shown"
|
||||||
|
headingText={ __(
|
||||||
|
'Revenue is now reported from paid orders ✅',
|
||||||
|
'woocommerce'
|
||||||
|
) }
|
||||||
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,11 @@ export const config = applyFilters( SETTINGS_FILTER, {
|
||||||
label: __( 'Date type:', 'woocommerce' ),
|
label: __( 'Date type:', 'woocommerce' ),
|
||||||
inputType: 'select',
|
inputType: 'select',
|
||||||
options: [
|
options: [
|
||||||
|
{
|
||||||
|
label: __( 'Select a date type', 'woocommerce' ),
|
||||||
|
value: '',
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: __( 'Date created', 'woocommerce' ),
|
label: __( 'Date created', 'woocommerce' ),
|
||||||
value: 'date_created',
|
value: 'date_created',
|
||||||
|
@ -143,6 +148,5 @@ export const config = applyFilters( SETTINGS_FILTER, {
|
||||||
'Database date field considered for Revenue and Orders reports',
|
'Database date field considered for Revenue and Orders reports',
|
||||||
'woocommerce'
|
'woocommerce'
|
||||||
),
|
),
|
||||||
defaultValue: 'date_created',
|
|
||||||
},
|
},
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
.woocommerce-revenue-report-date-tour {
|
||||||
|
h2.woocommerce-tour-kit-step__heading {
|
||||||
|
font-size: 1.5em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { TourKit, TourKitTypes } from '@woocommerce/components';
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
|
||||||
|
import {
|
||||||
|
createElement,
|
||||||
|
createInterpolateElement,
|
||||||
|
useState,
|
||||||
|
} from '@wordpress/element';
|
||||||
|
import { useSelect, useDispatch } from '@wordpress/data';
|
||||||
|
import { getAdminLink } from '@woocommerce/settings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import './report-date-tour.scss';
|
||||||
|
|
||||||
|
const DATE_TYPE_OPTION = 'woocommerce_date_type';
|
||||||
|
|
||||||
|
export const ReportDateTour: React.FC< {
|
||||||
|
optionName: string;
|
||||||
|
headingText: string;
|
||||||
|
} > = ( { optionName, headingText } ) => {
|
||||||
|
const [ isDismissed, setIsDismissed ] = useState( false );
|
||||||
|
const { updateOptions } = useDispatch( OPTIONS_STORE_NAME );
|
||||||
|
|
||||||
|
const { shouldShowTour, isResolving } = useSelect( ( select ) => {
|
||||||
|
const { getOption, hasFinishedResolution } =
|
||||||
|
select( OPTIONS_STORE_NAME );
|
||||||
|
return {
|
||||||
|
shouldShowTour:
|
||||||
|
getOption( optionName ) !== 'yes' &&
|
||||||
|
getOption( DATE_TYPE_OPTION ) === false,
|
||||||
|
isResolving: ! (
|
||||||
|
hasFinishedResolution( 'getOption', [ optionName ] ) &&
|
||||||
|
hasFinishedResolution( 'getOption', [ DATE_TYPE_OPTION ] )
|
||||||
|
),
|
||||||
|
};
|
||||||
|
} );
|
||||||
|
|
||||||
|
if ( isDismissed || ! shouldShowTour || isResolving ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config: TourKitTypes.WooConfig = {
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
referenceElements: {
|
||||||
|
desktop:
|
||||||
|
'.woocommerce-filters-filter > .components-dropdown',
|
||||||
|
},
|
||||||
|
focusElement: {
|
||||||
|
desktop:
|
||||||
|
'.woocommerce-filters-filter > .components-dropdown',
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
name: 'product-feedback-',
|
||||||
|
heading: headingText,
|
||||||
|
descriptions: {
|
||||||
|
desktop: createInterpolateElement(
|
||||||
|
__(
|
||||||
|
'We now collect orders in this table based on when the payment went through, rather than when they were placed. You can change this in <link>settings</link>.',
|
||||||
|
'woocommerce'
|
||||||
|
),
|
||||||
|
{
|
||||||
|
link: createElement( 'a', {
|
||||||
|
href: getAdminLink(
|
||||||
|
'admin.php?page=wc-admin&path=/analytics/settings'
|
||||||
|
),
|
||||||
|
'aria-label': __(
|
||||||
|
'Analytics date settings',
|
||||||
|
'woocommerce'
|
||||||
|
),
|
||||||
|
} ),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
},
|
||||||
|
primaryButton: {
|
||||||
|
text: __( 'Got it', 'woocommerce' ),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
classNames: {
|
||||||
|
desktop: 'woocommerce-revenue-report-date-tour',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
closeHandler: () => {
|
||||||
|
updateOptions( {
|
||||||
|
[ optionName ]: 'yes',
|
||||||
|
} );
|
||||||
|
setIsDismissed( true );
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return <TourKit config={ config } />;
|
||||||
|
};
|
|
@ -8,6 +8,10 @@ import {
|
||||||
Product,
|
Product,
|
||||||
ProductVariation,
|
ProductVariation,
|
||||||
} from '@woocommerce/data';
|
} from '@woocommerce/data';
|
||||||
|
import {
|
||||||
|
getProductStockStatus,
|
||||||
|
getProductStockStatusClass,
|
||||||
|
} from '@woocommerce/product-editor';
|
||||||
import {
|
import {
|
||||||
Link,
|
Link,
|
||||||
ListItem,
|
ListItem,
|
||||||
|
@ -31,10 +35,6 @@ import useVariationsOrder from '~/products/hooks/use-variations-order';
|
||||||
import HiddenIcon from '~/products/images/hidden-icon';
|
import HiddenIcon from '~/products/images/hidden-icon';
|
||||||
import VisibleIcon from '~/products/images/visible-icon';
|
import VisibleIcon from '~/products/images/visible-icon';
|
||||||
import { CurrencyContext } from '../../../lib/currency-context';
|
import { CurrencyContext } from '../../../lib/currency-context';
|
||||||
import {
|
|
||||||
getProductStockStatus,
|
|
||||||
getProductStockStatusClass,
|
|
||||||
} from '../../utils/get-product-stock-status';
|
|
||||||
import './variations.scss';
|
import './variations.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Link } from '@woocommerce/components';
|
||||||
import {
|
import {
|
||||||
__experimentalWooProductSectionItem as WooProductSectionItem,
|
__experimentalWooProductSectionItem as WooProductSectionItem,
|
||||||
__experimentalWooProductFieldItem as WooProductFieldItem,
|
__experimentalWooProductFieldItem as WooProductFieldItem,
|
||||||
Link,
|
__experimentalProductSectionLayout as ProductSectionLayout,
|
||||||
} from '@woocommerce/components';
|
} from '@woocommerce/product-editor';
|
||||||
import { __experimentalProductSectionLayout as ProductSectionLayout } from '@woocommerce/product-editor';
|
|
||||||
import { recordEvent } from '@woocommerce/tracks';
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
Link,
|
Link,
|
||||||
__experimentalTooltip as Tooltip,
|
__experimentalTooltip as Tooltip,
|
||||||
} from '@woocommerce/components';
|
} from '@woocommerce/components';
|
||||||
|
import { getCheckboxTracks } from '@woocommerce/product-editor';
|
||||||
import interpolateComponents from '@automattic/interpolate-components';
|
import interpolateComponents from '@automattic/interpolate-components';
|
||||||
import { Product } from '@woocommerce/data';
|
import { Product } from '@woocommerce/data';
|
||||||
import { recordEvent } from '@woocommerce/tracks';
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
@ -15,7 +16,6 @@ import { recordEvent } from '@woocommerce/tracks';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { getCheckboxTracks } from '../../sections/utils';
|
|
||||||
import { PRODUCT_DETAILS_SLUG } from '../constants';
|
import { PRODUCT_DETAILS_SLUG } from '../constants';
|
||||||
|
|
||||||
export const DetailsFeatureField = () => {
|
export const DetailsFeatureField = () => {
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import {
|
import {
|
||||||
|
__experimentalProductFieldSection as ProductFieldSection,
|
||||||
__experimentalWooProductSectionItem as WooProductSectionItem,
|
__experimentalWooProductSectionItem as WooProductSectionItem,
|
||||||
__experimentalWooProductFieldItem as WooProductFieldItem,
|
__experimentalWooProductFieldItem as WooProductFieldItem,
|
||||||
} from '@woocommerce/components';
|
} from '@woocommerce/product-editor';
|
||||||
import { __experimentalProductFieldSection as ProductFieldSection } from '@woocommerce/product-editor';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Link } from '@woocommerce/components';
|
||||||
import {
|
import {
|
||||||
__experimentalWooProductSectionItem as WooProductSectionItem,
|
__experimentalWooProductSectionItem as WooProductSectionItem,
|
||||||
__experimentalWooProductFieldItem as WooProductFieldItem,
|
__experimentalWooProductFieldItem as WooProductFieldItem,
|
||||||
Link,
|
__experimentalProductFieldSection as ProductFieldSection,
|
||||||
} from '@woocommerce/components';
|
} from '@woocommerce/product-editor';
|
||||||
import { __experimentalProductFieldSection as ProductFieldSection } from '@woocommerce/product-editor';
|
|
||||||
import { recordEvent } from '@woocommerce/tracks';
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,13 +4,9 @@
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { useFormContext } from '@woocommerce/components';
|
import { useFormContext } from '@woocommerce/components';
|
||||||
import { CheckboxControl } from '@wordpress/components';
|
import { CheckboxControl } from '@wordpress/components';
|
||||||
|
import { getCheckboxTracks } from '@woocommerce/product-editor';
|
||||||
import { Product } from '@woocommerce/data';
|
import { Product } from '@woocommerce/data';
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import { getCheckboxTracks } from '../../sections/utils';
|
|
||||||
|
|
||||||
export const InventoryStockLimitField = () => {
|
export const InventoryStockLimitField = () => {
|
||||||
const { getCheckboxControlProps } = useFormContext< Product >();
|
const { getCheckboxControlProps } = useFormContext< Product >();
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
useFormContext,
|
useFormContext,
|
||||||
__experimentalConditionalWrapper as ConditionalWrapper,
|
__experimentalConditionalWrapper as ConditionalWrapper,
|
||||||
} from '@woocommerce/components';
|
} from '@woocommerce/components';
|
||||||
|
import { getCheckboxTracks } from '@woocommerce/product-editor';
|
||||||
import { Tooltip, ToggleControl } from '@wordpress/components';
|
import { Tooltip, ToggleControl } from '@wordpress/components';
|
||||||
import { Product } from '@woocommerce/data';
|
import { Product } from '@woocommerce/data';
|
||||||
|
|
||||||
|
@ -13,7 +14,6 @@ import { Product } from '@woocommerce/data';
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { getAdminSetting } from '~/utils/admin-settings';
|
import { getAdminSetting } from '~/utils/admin-settings';
|
||||||
import { getCheckboxTracks } from '../../sections/utils';
|
|
||||||
|
|
||||||
export const InventoryTrackQuantityField = () => {
|
export const InventoryTrackQuantityField = () => {
|
||||||
const { getCheckboxControlProps } = useFormContext< Product >();
|
const { getCheckboxControlProps } = useFormContext< Product >();
|
||||||
|
|
|
@ -3,13 +3,15 @@
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import {
|
import {
|
||||||
__experimentalWooProductSectionItem as WooProductSectionItem,
|
|
||||||
__experimentalWooProductFieldItem as WooProductFieldItem,
|
|
||||||
Link,
|
Link,
|
||||||
useFormContext,
|
useFormContext,
|
||||||
CollapsibleContent,
|
CollapsibleContent,
|
||||||
} from '@woocommerce/components';
|
} from '@woocommerce/components';
|
||||||
import { __experimentalProductSectionLayout as ProductSectionLayout } from '@woocommerce/product-editor';
|
import {
|
||||||
|
__experimentalWooProductSectionItem as WooProductSectionItem,
|
||||||
|
__experimentalWooProductFieldItem as WooProductFieldItem,
|
||||||
|
__experimentalProductSectionLayout as ProductSectionLayout,
|
||||||
|
} from '@woocommerce/product-editor';
|
||||||
import { Card, CardBody } from '@wordpress/components';
|
import { Card, CardBody } from '@wordpress/components';
|
||||||
import { recordEvent } from '@woocommerce/tracks';
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
import { getAdminLink } from '@woocommerce/settings';
|
import { getAdminLink } from '@woocommerce/settings';
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { formatCurrencyDisplayValue } from '@woocommerce/product-editor';
|
||||||
import { useFormContext, Link } from '@woocommerce/components';
|
import { useFormContext, Link } from '@woocommerce/components';
|
||||||
import { recordEvent } from '@woocommerce/tracks';
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
import { useContext } from '@wordpress/element';
|
import { useContext } from '@wordpress/element';
|
||||||
|
@ -18,7 +19,6 @@ import {
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { CurrencyInputProps } from './pricing-section-fills';
|
import { CurrencyInputProps } from './pricing-section-fills';
|
||||||
import { formatCurrencyDisplayValue } from '../../sections/utils';
|
|
||||||
import { CurrencyContext } from '../../../lib/currency-context';
|
import { CurrencyContext } from '../../../lib/currency-context';
|
||||||
import { ADMIN_URL } from '~/utils/admin-settings';
|
import { ADMIN_URL } from '~/utils/admin-settings';
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { Product, OPTIONS_STORE_NAME } from '@woocommerce/data';
|
||||||
import { useSelect } from '@wordpress/data';
|
import { useSelect } from '@wordpress/data';
|
||||||
import interpolateComponents from '@automattic/interpolate-components';
|
import interpolateComponents from '@automattic/interpolate-components';
|
||||||
import { format as formatDate } from '@wordpress/date';
|
import { format as formatDate } from '@wordpress/date';
|
||||||
|
import { formatCurrencyDisplayValue } from '@woocommerce/product-editor';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {
|
import {
|
||||||
BaseControl,
|
BaseControl,
|
||||||
|
@ -26,7 +27,6 @@ import {
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { CurrencyInputProps } from './pricing-section-fills';
|
import { CurrencyInputProps } from './pricing-section-fills';
|
||||||
import { formatCurrencyDisplayValue } from '../../sections/utils';
|
|
||||||
import { CurrencyContext } from '../../../lib/currency-context';
|
import { CurrencyContext } from '../../../lib/currency-context';
|
||||||
|
|
||||||
type PricingListFieldProps = {
|
type PricingListFieldProps = {
|
||||||
|
|
|
@ -3,13 +3,15 @@
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import {
|
import {
|
||||||
__experimentalWooProductSectionItem as WooProductSectionItem,
|
|
||||||
__experimentalWooProductFieldItem as WooProductFieldItem,
|
|
||||||
Link,
|
Link,
|
||||||
useFormContext,
|
useFormContext,
|
||||||
CollapsibleContent,
|
CollapsibleContent,
|
||||||
} from '@woocommerce/components';
|
} from '@woocommerce/components';
|
||||||
import { __experimentalProductSectionLayout as ProductSectionLayout } from '@woocommerce/product-editor';
|
import {
|
||||||
|
__experimentalWooProductSectionItem as WooProductSectionItem,
|
||||||
|
__experimentalWooProductFieldItem as WooProductFieldItem,
|
||||||
|
__experimentalProductSectionLayout as ProductSectionLayout,
|
||||||
|
} from '@woocommerce/product-editor';
|
||||||
import { recordEvent } from '@woocommerce/tracks';
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
import { Product } from '@woocommerce/data';
|
import { Product } from '@woocommerce/data';
|
||||||
import { useContext } from '@wordpress/element';
|
import { useContext } from '@wordpress/element';
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import {
|
import { renderField, useFormContext } from '@woocommerce/components';
|
||||||
__experimentalWooProductFieldItem as WooProductFieldItem,
|
import { __experimentalWooProductFieldItem as WooProductFieldItem } from '@woocommerce/product-editor';
|
||||||
renderField,
|
|
||||||
useFormContext,
|
|
||||||
} from '@woocommerce/components';
|
|
||||||
import { Product, ProductFormField } from '@woocommerce/data';
|
import { Product, ProductFormField } from '@woocommerce/data';
|
||||||
|
|
||||||
export const Fields: React.FC< { fields: ProductFormField[] } > = ( {
|
export const Fields: React.FC< { fields: ProductFormField[] } > = ( {
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { __experimentalWooProductSectionItem as WooProductSectionItem } from '@woocommerce/components';
|
import {
|
||||||
import { __experimentalProductFieldSection as ProductFieldSection } from '@woocommerce/product-editor';
|
__experimentalProductFieldSection as ProductFieldSection,
|
||||||
|
__experimentalWooProductSectionItem as WooProductSectionItem,
|
||||||
|
} from '@woocommerce/product-editor';
|
||||||
import { ProductFormSection } from '@woocommerce/data';
|
import { ProductFormSection } from '@woocommerce/data';
|
||||||
|
|
||||||
export const Sections: React.FC< { sections: ProductFormSection[] } > = ( {
|
export const Sections: React.FC< { sections: ProductFormSection[] } > = ( {
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { registerPlugin } from '@wordpress/plugins';
|
||||||
import {
|
import {
|
||||||
__experimentalWooProductTabItem as WooProductTabItem,
|
__experimentalWooProductTabItem as WooProductTabItem,
|
||||||
__experimentalWooProductSectionItem as WooProductSectionItem,
|
__experimentalWooProductSectionItem as WooProductSectionItem,
|
||||||
useFormContext,
|
} from '@woocommerce/product-editor';
|
||||||
} from '@woocommerce/components';
|
import { useFormContext } from '@woocommerce/components';
|
||||||
import { Product } from '@woocommerce/data';
|
import { Product } from '@woocommerce/data';
|
||||||
import { useMemo } from '@wordpress/element';
|
import { useMemo } from '@wordpress/element';
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { registerPlugin } from '@wordpress/plugins';
|
||||||
import {
|
import {
|
||||||
__experimentalWooProductTabItem as WooProductTabItem,
|
__experimentalWooProductTabItem as WooProductTabItem,
|
||||||
__experimentalWooProductSectionItem as WooProductSectionItem,
|
__experimentalWooProductSectionItem as WooProductSectionItem,
|
||||||
} from '@woocommerce/components';
|
} from '@woocommerce/product-editor';
|
||||||
import { PartialProduct } from '@woocommerce/data';
|
import { PartialProduct } from '@woocommerce/data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,8 +5,8 @@ import { __ } from '@wordpress/i18n';
|
||||||
import {
|
import {
|
||||||
__experimentalWooProductSectionItem as WooProductSectionItem,
|
__experimentalWooProductSectionItem as WooProductSectionItem,
|
||||||
__experimentalWooProductFieldItem as WooProductFieldItem,
|
__experimentalWooProductFieldItem as WooProductFieldItem,
|
||||||
} from '@woocommerce/components';
|
__experimentalProductSectionLayout as ProductSectionLayout,
|
||||||
import { __experimentalProductSectionLayout as ProductSectionLayout } from '@woocommerce/product-editor';
|
} from '@woocommerce/product-editor';
|
||||||
import { PartialProduct, OPTIONS_STORE_NAME } from '@woocommerce/data';
|
import { PartialProduct, OPTIONS_STORE_NAME } from '@woocommerce/data';
|
||||||
import { useSelect } from '@wordpress/data';
|
import { useSelect } from '@wordpress/data';
|
||||||
import { useState } from '@wordpress/element';
|
import { useState } from '@wordpress/element';
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
|
import { AUTO_DRAFT_NAME } from '@woocommerce/product-editor';
|
||||||
import { useDispatch } from '@wordpress/data';
|
import { useDispatch } from '@wordpress/data';
|
||||||
import { useCallback, useState } from '@wordpress/element';
|
import { useCallback, useState } from '@wordpress/element';
|
||||||
import {
|
import {
|
||||||
|
@ -10,11 +11,6 @@ import {
|
||||||
} from '@woocommerce/data';
|
} from '@woocommerce/data';
|
||||||
import { useFormContext } from '@woocommerce/components';
|
import { useFormContext } from '@woocommerce/components';
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import { AUTO_DRAFT_NAME } from '../utils/get-product-title';
|
|
||||||
|
|
||||||
export function useProductVariationsHelper() {
|
export function useProductVariationsHelper() {
|
||||||
const {
|
const {
|
||||||
generateProductVariations: _generateProductVariations,
|
generateProductVariations: _generateProductVariations,
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { __ } from '@wordpress/i18n';
|
||||||
import { useEffect } from '@wordpress/element';
|
import { useEffect } from '@wordpress/element';
|
||||||
import { TabPanel, Tooltip } from '@wordpress/components';
|
import { TabPanel, Tooltip } from '@wordpress/components';
|
||||||
import { navigateTo, getNewPath, getQuery } from '@woocommerce/navigation';
|
import { navigateTo, getNewPath, getQuery } from '@woocommerce/navigation';
|
||||||
import { __experimentalWooProductTabItem as WooProductTabItem } from '@woocommerce/components';
|
import { __experimentalWooProductTabItem as WooProductTabItem } from '@woocommerce/product-editor';
|
||||||
import { PartialProduct } from '@woocommerce/data';
|
import { PartialProduct } from '@woocommerce/data';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
import { chevronDown, check, Icon } from '@wordpress/icons';
|
import { chevronDown, check, Icon } from '@wordpress/icons';
|
||||||
import { registerPlugin } from '@wordpress/plugins';
|
import { registerPlugin } from '@wordpress/plugins';
|
||||||
import { useFormContext } from '@woocommerce/components';
|
import { useFormContext } from '@woocommerce/components';
|
||||||
|
import { preventLeavingProductForm } from '@woocommerce/product-editor';
|
||||||
import { Product } from '@woocommerce/data';
|
import { Product } from '@woocommerce/data';
|
||||||
import { recordEvent } from '@woocommerce/tracks';
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
import { navigateTo } from '@woocommerce/navigation';
|
import { navigateTo } from '@woocommerce/navigation';
|
||||||
|
@ -24,7 +25,6 @@ import { store } from '@wordpress/viewport';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { preventLeavingProductForm } from './utils/prevent-leaving-product-form';
|
|
||||||
import usePreventLeavingPage from '~/hooks/usePreventLeavingPage';
|
import usePreventLeavingPage from '~/hooks/usePreventLeavingPage';
|
||||||
import { WooHeaderItem } from '~/header/utils';
|
import { WooHeaderItem } from '~/header/utils';
|
||||||
import { useProductHelper } from './use-product-helper';
|
import { useProductHelper } from './use-product-helper';
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
TextControl,
|
TextControl,
|
||||||
} from '@wordpress/components';
|
} from '@wordpress/components';
|
||||||
import { closeSmall, cog } from '@wordpress/icons';
|
import { closeSmall, cog } from '@wordpress/icons';
|
||||||
|
import { getCheckboxTracks } from '@woocommerce/product-editor';
|
||||||
import { Product } from '@woocommerce/data';
|
import { Product } from '@woocommerce/data';
|
||||||
import { useFormContext } from '@woocommerce/components';
|
import { useFormContext } from '@woocommerce/components';
|
||||||
import { useState } from '@wordpress/element';
|
import { useState } from '@wordpress/element';
|
||||||
|
@ -18,7 +19,6 @@ import { useState } from '@wordpress/element';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { getCheckboxTracks } from '../sections/utils';
|
|
||||||
import { WooHeaderItem } from '~/header/utils';
|
import { WooHeaderItem } from '~/header/utils';
|
||||||
import './product-settings.scss';
|
import './product-settings.scss';
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,14 @@ import { Pill } from '@woocommerce/components';
|
||||||
import { PRODUCTS_STORE_NAME, WCDataSelector } from '@woocommerce/data';
|
import { PRODUCTS_STORE_NAME, WCDataSelector } from '@woocommerce/data';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { useSelect } from '@wordpress/data';
|
import { useSelect } from '@wordpress/data';
|
||||||
|
import {
|
||||||
|
getProductStatus,
|
||||||
|
PRODUCT_STATUS_LABELS,
|
||||||
|
} from '@woocommerce/product-editor';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import {
|
|
||||||
getProductStatus,
|
|
||||||
PRODUCT_STATUS_LABELS,
|
|
||||||
} from './utils/get-product-status';
|
|
||||||
import './product-status-badge.scss';
|
import './product-status-badge.scss';
|
||||||
|
|
||||||
export const ProductStatusBadge: React.FC = () => {
|
export const ProductStatusBadge: React.FC = () => {
|
||||||
|
|
|
@ -10,6 +10,11 @@ import {
|
||||||
} from '@woocommerce/data';
|
} from '@woocommerce/data';
|
||||||
import { getAdminLink } from '@woocommerce/settings';
|
import { getAdminLink } from '@woocommerce/settings';
|
||||||
import { getNewPath } from '@woocommerce/navigation';
|
import { getNewPath } from '@woocommerce/navigation';
|
||||||
|
import {
|
||||||
|
getProductTitle,
|
||||||
|
getProductVariationTitle,
|
||||||
|
getTruncatedProductVariationTitle,
|
||||||
|
} from '@woocommerce/product-editor';
|
||||||
import { useFormContext } from '@woocommerce/components';
|
import { useFormContext } from '@woocommerce/components';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { useSelect } from '@wordpress/data';
|
import { useSelect } from '@wordpress/data';
|
||||||
|
@ -17,11 +22,6 @@ import { useSelect } from '@wordpress/data';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { getProductTitle } from './utils/get-product-title';
|
|
||||||
import {
|
|
||||||
getProductVariationTitle,
|
|
||||||
getTruncatedProductVariationTitle,
|
|
||||||
} from './utils/get-product-variation-title';
|
|
||||||
import { ProductBreadcrumbs } from './product-breadcrumbs';
|
import { ProductBreadcrumbs } from './product-breadcrumbs';
|
||||||
import { ProductStatusBadge } from './product-status-badge';
|
import { ProductStatusBadge } from './product-status-badge';
|
||||||
import { WooHeaderPageTitle } from '~/header/utils';
|
import { WooHeaderPageTitle } from '~/header/utils';
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
EXPERIMENTAL_PRODUCT_VARIATIONS_STORE_NAME,
|
EXPERIMENTAL_PRODUCT_VARIATIONS_STORE_NAME,
|
||||||
ProductVariation,
|
ProductVariation,
|
||||||
} from '@woocommerce/data';
|
} from '@woocommerce/data';
|
||||||
|
import { preventLeavingProductForm } from '@woocommerce/product-editor';
|
||||||
import { registerPlugin } from '@wordpress/plugins';
|
import { registerPlugin } from '@wordpress/plugins';
|
||||||
import { useDispatch } from '@wordpress/data';
|
import { useDispatch } from '@wordpress/data';
|
||||||
import { useFormContext } from '@woocommerce/components';
|
import { useFormContext } from '@woocommerce/components';
|
||||||
|
@ -16,7 +17,6 @@ import { useState } from '@wordpress/element';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { preventLeavingProductForm } from './utils/prevent-leaving-product-form';
|
|
||||||
import usePreventLeavingPage from '~/hooks/usePreventLeavingPage';
|
import usePreventLeavingPage from '~/hooks/usePreventLeavingPage';
|
||||||
import { WooHeaderItem } from '~/header/utils';
|
import { WooHeaderItem } from '~/header/utils';
|
||||||
import './product-form-actions.scss';
|
import './product-form-actions.scss';
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
CardBody,
|
CardBody,
|
||||||
BaseControl,
|
BaseControl,
|
||||||
} from '@wordpress/components';
|
} from '@wordpress/components';
|
||||||
|
import { getCheckboxTracks } from '@woocommerce/product-editor';
|
||||||
import { MediaItem } from '@wordpress/media-utils';
|
import { MediaItem } from '@wordpress/media-utils';
|
||||||
import {
|
import {
|
||||||
useFormContext,
|
useFormContext,
|
||||||
|
@ -21,7 +22,6 @@ import { useState } from '@wordpress/element';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { getCheckboxTracks } from './utils';
|
|
||||||
import { ProductSectionLayout } from '../layout/product-section-layout';
|
import { ProductSectionLayout } from '../layout/product-section-layout';
|
||||||
import { SingleImageField } from '../fields/single-image-field';
|
import { SingleImageField } from '../fields/single-image-field';
|
||||||
|
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { ChangeEvent } from 'react';
|
|
||||||
import { Product } from '@woocommerce/data';
|
|
||||||
import { recordEvent } from '@woocommerce/tracks';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import { NUMBERS_AND_ALLOWED_CHARS } from '../constants';
|
|
||||||
|
|
||||||
type CurrencyConfig = {
|
|
||||||
code: string;
|
|
||||||
symbol: string;
|
|
||||||
symbolPosition: string;
|
|
||||||
decimalSeparator: string;
|
|
||||||
priceFormat: string;
|
|
||||||
thousandSeparator: string;
|
|
||||||
precision: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get additional props to be passed to all checkbox inputs.
|
|
||||||
*
|
|
||||||
* @param name Name of the checkbox.
|
|
||||||
* @return Props.
|
|
||||||
*/
|
|
||||||
export function getCheckboxTracks< T = Product >( name: string ) {
|
|
||||||
return {
|
|
||||||
onChange: (
|
|
||||||
isChecked: ChangeEvent< HTMLInputElement > | T[ keyof T ]
|
|
||||||
) => {
|
|
||||||
recordEvent( `product_checkbox_${ name }`, {
|
|
||||||
checked: isChecked,
|
|
||||||
} );
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get input props for currency related values and symbol positions.
|
|
||||||
*
|
|
||||||
* @param {Object} currencyConfig - Currency context
|
|
||||||
* @return {Object} Props.
|
|
||||||
*/
|
|
||||||
export const getCurrencySymbolProps = ( currencyConfig: CurrencyConfig ) => {
|
|
||||||
const { symbol, symbolPosition } = currencyConfig;
|
|
||||||
const currencyPosition = symbolPosition.includes( 'left' )
|
|
||||||
? 'prefix'
|
|
||||||
: 'suffix';
|
|
||||||
|
|
||||||
return {
|
|
||||||
[ currencyPosition ]: symbol,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleans and formats the currency value shown to the user.
|
|
||||||
*
|
|
||||||
* @param {string} value Form value.
|
|
||||||
* @param {Object} currencyConfig Currency context.
|
|
||||||
* @return {string} Display value.
|
|
||||||
*/
|
|
||||||
export const formatCurrencyDisplayValue = (
|
|
||||||
value: string,
|
|
||||||
currencyConfig: CurrencyConfig,
|
|
||||||
format: ( number: number | string ) => string
|
|
||||||
) => {
|
|
||||||
const { decimalSeparator, thousandSeparator } = currencyConfig;
|
|
||||||
|
|
||||||
const regex = new RegExp(
|
|
||||||
NUMBERS_AND_ALLOWED_CHARS.replace( '%s1', decimalSeparator ).replace(
|
|
||||||
'%s2',
|
|
||||||
thousandSeparator
|
|
||||||
),
|
|
||||||
'g'
|
|
||||||
);
|
|
||||||
|
|
||||||
return value === undefined ? value : format( value ).replace( regex, '' );
|
|
||||||
};
|
|
|
@ -2,6 +2,10 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import {
|
||||||
|
AUTO_DRAFT_NAME,
|
||||||
|
getDerivedProductType,
|
||||||
|
} from '@woocommerce/product-editor';
|
||||||
import { useDispatch } from '@wordpress/data';
|
import { useDispatch } from '@wordpress/data';
|
||||||
import { useCallback, useContext, useState } from '@wordpress/element';
|
import { useCallback, useContext, useState } from '@wordpress/element';
|
||||||
import * as WooNumber from '@woocommerce/number';
|
import * as WooNumber from '@woocommerce/number';
|
||||||
|
@ -20,9 +24,7 @@ import { recordEvent } from '@woocommerce/tracks';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { AUTO_DRAFT_NAME } from './utils/get-product-title';
|
|
||||||
import { CurrencyContext } from '../lib/currency-context';
|
import { CurrencyContext } from '../lib/currency-context';
|
||||||
import { getDerivedProductType } from './utils/get-derived-product-type';
|
|
||||||
import {
|
import {
|
||||||
NUMBERS_AND_DECIMAL_SEPARATOR,
|
NUMBERS_AND_DECIMAL_SEPARATOR,
|
||||||
ONLY_ONE_DECIMAL_SEPARATOR,
|
ONLY_ONE_DECIMAL_SEPARATOR,
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
"clean": "rimraf ../woocommerce/assets/client/admin/*",
|
"clean": "rimraf ../woocommerce/assets/client/admin/*",
|
||||||
"client:watch": "cross-env WC_ADMIN_PHASE=development pnpm run build:feature-config && cross-env WC_ADMIN_PHASE=development webpack --watch",
|
"client:watch": "cross-env WC_ADMIN_PHASE=development pnpm run build:feature-config && cross-env WC_ADMIN_PHASE=development webpack --watch",
|
||||||
"create-hook-reference": "node ./bin/hook-reference/index.js",
|
"create-hook-reference": "node ./bin/hook-reference/index.js",
|
||||||
"create-wc-extension": "node ./bin/starter-pack/starter-pack.js",
|
"create-wc-extension": "pnpm -w run create-extension",
|
||||||
"dev": "cross-env WC_ADMIN_PHASE=development pnpm run build:feature-config && cross-env WC_ADMIN_PHASE=development pnpm -w run build --filter='./packages/js/*' && cross-env WC_ADMIN_PHASE=development webpack",
|
"dev": "cross-env WC_ADMIN_PHASE=development pnpm run build:feature-config && cross-env WC_ADMIN_PHASE=development pnpm -w run build --filter='./packages/js/*' && cross-env WC_ADMIN_PHASE=development webpack",
|
||||||
"example": "webpack --config docs/examples/extensions/examples.config.js --watch",
|
"example": "webpack --config docs/examples/extensions/examples.config.js --watch",
|
||||||
"preinstall": "npx only-allow pnpm",
|
"preinstall": "npx only-allow pnpm",
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: add
|
||||||
|
|
||||||
|
Add a cache for orders, to use when custom order tables are enabled
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: fix
|
||||||
|
|
||||||
|
Fix no enforcing of min/max limits in quantity selector of variable products.
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: update
|
||||||
|
|
||||||
|
Update create-wc-extension script within woocommerce-admin.
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: fix
|
||||||
|
|
||||||
|
Add support for null inputs to pnpm wc_add_number_precision
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: tweak
|
||||||
|
|
||||||
|
Trigger event `woocommerce_attributes_saved` following successful product meta box ajax update.
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: major
|
||||||
|
Type: update
|
||||||
|
|
||||||
|
Change the default date used on Revenue and Orders report to 'date_paid' and create spotlight on both reports
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: update
|
||||||
|
|
||||||
|
Update imports of product slot fills to new @woocommerce/product-editor library
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Move product utils into product editor package
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: update
|
||||||
|
|
||||||
|
Update WooCommerce Blocks to 9.6.2
|
|
@ -797,6 +797,9 @@ jQuery( function ( $ ) {
|
||||||
$( '#variable_product_options' ).trigger( 'reload' );
|
$( '#variable_product_options' ).trigger( 'reload' );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$( document.body ).trigger( 'woocommerce_attributes_saved' );
|
||||||
|
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -263,7 +263,8 @@
|
||||||
$dimensions = form.$product.find(
|
$dimensions = form.$product.find(
|
||||||
'.product_dimensions, .woocommerce-product-attributes-item--dimensions .woocommerce-product-attributes-item__value'
|
'.product_dimensions, .woocommerce-product-attributes-item--dimensions .woocommerce-product-attributes-item__value'
|
||||||
),
|
),
|
||||||
$qty = form.$singleVariationWrap.find( '.quantity input[name="quantity"]' ),
|
$qty_input = form.$singleVariationWrap.find( '.quantity input.qty[name="quantity"]' ),
|
||||||
|
$qty = $qty_input.closest( '.quantity' ),
|
||||||
purchasable = true,
|
purchasable = true,
|
||||||
variation_id = '',
|
variation_id = '',
|
||||||
template = false,
|
template = false,
|
||||||
|
@ -308,12 +309,11 @@
|
||||||
|
|
||||||
// Hide or show qty input
|
// Hide or show qty input
|
||||||
if ( variation.is_sold_individually === 'yes' ) {
|
if ( variation.is_sold_individually === 'yes' ) {
|
||||||
$qty.find( 'input.qty' ).val( '1' ).attr( 'min', '1' ).attr( 'max', '' ).trigger( 'change' );
|
$qty_input.val( '1' ).attr( 'min', '1' ).attr( 'max', '' ).trigger( 'change' );
|
||||||
$qty.hide();
|
$qty.hide();
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
var $qty_input = $qty.find( 'input.qty' ),
|
var qty_val = parseFloat( $qty_input.val() );
|
||||||
qty_val = parseFloat( $qty_input.val() );
|
|
||||||
|
|
||||||
if ( isNaN( qty_val ) ) {
|
if ( isNaN( qty_val ) ) {
|
||||||
qty_val = variation.min_qty;
|
qty_val = variation.min_qty;
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"maxmind-db/reader": "^1.11",
|
"maxmind-db/reader": "^1.11",
|
||||||
"pelago/emogrifier": "^6.0",
|
"pelago/emogrifier": "^6.0",
|
||||||
"woocommerce/action-scheduler": "3.5.4",
|
"woocommerce/action-scheduler": "3.5.4",
|
||||||
"woocommerce/woocommerce-blocks": "9.6.1"
|
"woocommerce/woocommerce-blocks": "9.6.2"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"automattic/jetpack-changelogger": "^3.3.0",
|
"automattic/jetpack-changelogger": "^3.3.0",
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "092f7722dc1ab046fc4204f3a8d6ef5e",
|
"content-hash": "3114b2ae01803fb5d37d2e848b566b8e",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "automattic/jetpack-autoloader",
|
"name": "automattic/jetpack-autoloader",
|
||||||
|
@ -628,16 +628,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "woocommerce/woocommerce-blocks",
|
"name": "woocommerce/woocommerce-blocks",
|
||||||
"version": "v9.6.1",
|
"version": "v9.6.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/woocommerce/woocommerce-blocks.git",
|
"url": "https://github.com/woocommerce/woocommerce-blocks.git",
|
||||||
"reference": "5fae67e162c5300ca5da5ebeb4403005492483ac"
|
"reference": "1eb30afa09310d0dcbe8be1222ac3e5214c60328"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-blocks/zipball/5fae67e162c5300ca5da5ebeb4403005492483ac",
|
"url": "https://api.github.com/repos/woocommerce/woocommerce-blocks/zipball/1eb30afa09310d0dcbe8be1222ac3e5214c60328",
|
||||||
"reference": "5fae67e162c5300ca5da5ebeb4403005492483ac",
|
"reference": "1eb30afa09310d0dcbe8be1222ac3e5214c60328",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -683,9 +683,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/woocommerce/woocommerce-blocks/issues",
|
"issues": "https://github.com/woocommerce/woocommerce-blocks/issues",
|
||||||
"source": "https://github.com/woocommerce/woocommerce-blocks/tree/v9.6.1"
|
"source": "https://github.com/woocommerce/woocommerce-blocks/tree/v9.6.2"
|
||||||
},
|
},
|
||||||
"time": "2023-02-17T14:57:23+00:00"
|
"time": "2023-02-22T13:56:14+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
|
|
|
@ -10,9 +10,11 @@
|
||||||
* @package WooCommerce\Classes
|
* @package WooCommerce\Classes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Automattic\WooCommerce\Caches\OrderCache;
|
||||||
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
||||||
use Automattic\WooCommerce\Utilities\ArrayUtil;
|
use Automattic\WooCommerce\Utilities\ArrayUtil;
|
||||||
use Automattic\WooCommerce\Utilities\NumberUtil;
|
use Automattic\WooCommerce\Utilities\NumberUtil;
|
||||||
|
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||||
|
|
||||||
defined( 'ABSPATH' ) || exit;
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
@ -203,6 +205,11 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
||||||
|
|
||||||
$this->save_items();
|
$this->save_items();
|
||||||
|
|
||||||
|
if ( OrderUtil::orders_cache_usage_is_enabled() ) {
|
||||||
|
$order_cache = wc_get_container()->get( OrderCache::class );
|
||||||
|
$order_cache->update_if_cached( $this );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger action after saving to the DB.
|
* Trigger action after saving to the DB.
|
||||||
*
|
*
|
||||||
|
|
|
@ -488,6 +488,8 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
|
||||||
$visibility_class[] = 'show_options_if_checked';
|
$visibility_class[] = 'show_options_if_checked';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$must_disable = ArrayUtil::get_value_or_default( $value, 'disabled', false );
|
||||||
|
|
||||||
if ( ! isset( $value['checkboxgroup'] ) || 'start' === $value['checkboxgroup'] ) {
|
if ( ! isset( $value['checkboxgroup'] ) || 'start' === $value['checkboxgroup'] ) {
|
||||||
$has_tooltip = isset( $value['tooltip'] ) && '' !== $value['tooltip'];
|
$has_tooltip = isset( $value['tooltip'] ) && '' !== $value['tooltip'];
|
||||||
$tooltip_container_class = $has_tooltip ? 'with-tooltip' : '';
|
$tooltip_container_class = $has_tooltip ? 'with-tooltip' : '';
|
||||||
|
@ -515,6 +517,7 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
|
||||||
?>
|
?>
|
||||||
<label for="<?php echo esc_attr( $value['id'] ); ?>">
|
<label for="<?php echo esc_attr( $value['id'] ); ?>">
|
||||||
<input
|
<input
|
||||||
|
<?php echo $must_disable ? 'disabled' : ''; ?>
|
||||||
name="<?php echo esc_attr( $value['field_name'] ); ?>"
|
name="<?php echo esc_attr( $value['field_name'] ); ?>"
|
||||||
id="<?php echo esc_attr( $value['id'] ); ?>"
|
id="<?php echo esc_attr( $value['id'] ); ?>"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
|
|
@ -5,12 +5,15 @@
|
||||||
* @package WooCommerce\Classes
|
* @package WooCommerce\Classes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Automattic\WooCommerce\Caching\CacheNameSpaceTrait;
|
||||||
|
|
||||||
defined( 'ABSPATH' ) || exit;
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WC_Cache_Helper.
|
* WC_Cache_Helper.
|
||||||
*/
|
*/
|
||||||
class WC_Cache_Helper {
|
class WC_Cache_Helper {
|
||||||
|
use CacheNameSpaceTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transients to delete on shutdown.
|
* Transients to delete on shutdown.
|
||||||
|
@ -42,7 +45,7 @@ class WC_Cache_Helper {
|
||||||
*/
|
*/
|
||||||
public static function additional_nocache_headers( $headers ) {
|
public static function additional_nocache_headers( $headers ) {
|
||||||
global $wp_query;
|
global $wp_query;
|
||||||
|
|
||||||
$agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
$agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||||
|
|
||||||
$set_cache = false;
|
$set_cache = false;
|
||||||
|
@ -114,44 +117,6 @@ class WC_Cache_Helper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get prefix for use with wp_cache_set. Allows all cache in a group to be invalidated at once.
|
|
||||||
*
|
|
||||||
* @param string $group Group of cache to get.
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function get_cache_prefix( $group ) {
|
|
||||||
// Get cache key - uses cache key wc_orders_cache_prefix to invalidate when needed.
|
|
||||||
$prefix = wp_cache_get( 'wc_' . $group . '_cache_prefix', $group );
|
|
||||||
|
|
||||||
if ( false === $prefix ) {
|
|
||||||
$prefix = microtime();
|
|
||||||
wp_cache_set( 'wc_' . $group . '_cache_prefix', $prefix, $group );
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'wc_cache_' . $prefix . '_';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increment group cache prefix (invalidates cache).
|
|
||||||
*
|
|
||||||
* @param string $group Group of cache to clear.
|
|
||||||
*/
|
|
||||||
public static function incr_cache_prefix( $group ) {
|
|
||||||
wc_deprecated_function( 'WC_Cache_Helper::incr_cache_prefix', '3.9.0', 'WC_Cache_Helper::invalidate_cache_group' );
|
|
||||||
self::invalidate_cache_group( $group );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidate cache group.
|
|
||||||
*
|
|
||||||
* @param string $group Group of cache to clear.
|
|
||||||
* @since 3.9.0
|
|
||||||
*/
|
|
||||||
public static function invalidate_cache_group( $group ) {
|
|
||||||
wp_cache_set( 'wc_' . $group . '_cache_prefix', microtime(), $group );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a hash of the customer location.
|
* Get a hash of the customer location.
|
||||||
*
|
*
|
||||||
|
|
|
@ -8,10 +8,11 @@
|
||||||
* @package WooCommerce\Classes
|
* @package WooCommerce\Classes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defined( 'ABSPATH' ) || exit;
|
use Automattic\WooCommerce\Caches\OrderCache;
|
||||||
|
|
||||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Order factory class
|
* Order factory class
|
||||||
*/
|
*/
|
||||||
|
@ -30,13 +31,26 @@ class WC_Order_Factory {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$use_orders_cache = OrderUtil::orders_cache_usage_is_enabled();
|
||||||
|
if ( $use_orders_cache ) {
|
||||||
|
$order_cache = wc_get_container()->get( OrderCache::class );
|
||||||
|
$order = $order_cache->get( $order_id );
|
||||||
|
if ( ! is_null( $order ) ) {
|
||||||
|
return $order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$classname = self::get_class_name_for_order_id( $order_id );
|
$classname = self::get_class_name_for_order_id( $order_id );
|
||||||
if ( ! $classname ) {
|
if ( ! $classname ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new $classname( $order_id );
|
$order = new $classname( $order_id );
|
||||||
|
if ( $use_orders_cache && $order instanceof \WC_Abstract_Legacy_Order ) {
|
||||||
|
$order_cache->set( $order, $order_id );
|
||||||
|
}
|
||||||
|
return $order;
|
||||||
} catch ( Exception $e ) {
|
} catch ( Exception $e ) {
|
||||||
wc_caught_exception( $e, __FUNCTION__, array( $order_id ) );
|
wc_caught_exception( $e, __FUNCTION__, array( $order_id ) );
|
||||||
return false;
|
return false;
|
||||||
|
@ -56,6 +70,22 @@ class WC_Order_Factory {
|
||||||
$result = array();
|
$result = array();
|
||||||
$order_ids = array_filter( array_map( array( __CLASS__, 'get_order_id' ), $order_ids ) );
|
$order_ids = array_filter( array_map( array( __CLASS__, 'get_order_id' ), $order_ids ) );
|
||||||
|
|
||||||
|
$already_cached_orders = array();
|
||||||
|
$use_orders_cache = OrderUtil::orders_cache_usage_is_enabled();
|
||||||
|
if ( $use_orders_cache ) {
|
||||||
|
$uncached_order_ids = array();
|
||||||
|
$order_cache = wc_get_container()->get( OrderCache::class );
|
||||||
|
foreach ( $order_ids as $order_id ) {
|
||||||
|
$cached_order = $order_cache->get( absint( $order_id ) );
|
||||||
|
if ( is_null( $cached_order ) ) {
|
||||||
|
$uncached_order_ids[] = $order_id;
|
||||||
|
} else {
|
||||||
|
$already_cached_orders[] = $cached_order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$order_ids = $uncached_order_ids;
|
||||||
|
}
|
||||||
|
|
||||||
// We separate order list by class, since their datastore might be different.
|
// We separate order list by class, since their datastore might be different.
|
||||||
$order_list_by_class = array();
|
$order_list_by_class = array();
|
||||||
$order_id_classnames = self::get_class_names_for_order_ids( $order_ids );
|
$order_id_classnames = self::get_class_names_for_order_ids( $order_ids );
|
||||||
|
@ -99,9 +129,16 @@ class WC_Order_Factory {
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore the sort order.
|
// restore the sort order.
|
||||||
$result = array_replace( array_flip( $order_ids ), $result );
|
$result = array_values( array_replace( array_flip( $order_ids ), $result ) );
|
||||||
|
|
||||||
return array_values( $result );
|
if ( $use_orders_cache ) {
|
||||||
|
foreach ( $result as $order ) {
|
||||||
|
$order_cache->set( $order );
|
||||||
|
}
|
||||||
|
return array_merge( $already_cached_orders, $result );
|
||||||
|
} else {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1897,28 +1897,33 @@ function wc_get_tax_rounding_mode() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get rounding precision for internal WC calculations.
|
* Get rounding precision for internal WC calculations.
|
||||||
* Will increase the precision of wc_get_price_decimals by 2 decimals, unless WC_ROUNDING_PRECISION is set to a higher number.
|
* Will return the value of wc_get_price_decimals increased by 2 decimals, with WC_ROUNDING_PRECISION being the minimum.
|
||||||
*
|
*
|
||||||
* @since 2.6.3
|
* @since 2.6.3
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
function wc_get_rounding_precision() {
|
function wc_get_rounding_precision() {
|
||||||
$precision = wc_get_price_decimals() + 2;
|
$precision = wc_get_price_decimals() + 2;
|
||||||
if ( absint( WC_ROUNDING_PRECISION ) > $precision ) {
|
if ( $precision < absint( WC_ROUNDING_PRECISION ) ) {
|
||||||
$precision = absint( WC_ROUNDING_PRECISION );
|
$precision = absint( WC_ROUNDING_PRECISION );
|
||||||
}
|
}
|
||||||
return $precision;
|
return $precision;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add precision to a number and return a number.
|
* Add precision to a number by moving the decimal point to the right as many places as indicated by wc_get_price_decimals().
|
||||||
|
* Optionally the result is rounded so that the total number of digits equals wc_get_rounding_precision() plus one.
|
||||||
*
|
*
|
||||||
* @since 3.2.0
|
* @since 3.2.0
|
||||||
* @param float $value Number to add precision to.
|
* @param float|null $value Number to add precision to.
|
||||||
* @param bool $round If should round after adding precision.
|
* @param bool $round If the result should be rounded.
|
||||||
* @return int|float
|
* @return int|float
|
||||||
*/
|
*/
|
||||||
function wc_add_number_precision( float $value, bool $round = true ) {
|
function wc_add_number_precision( ?float $value, bool $round = true ) {
|
||||||
|
if ( ! $value ) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
$cent_precision = pow( 10, wc_get_price_decimals() );
|
$cent_precision = pow( 10, wc_get_price_decimals() );
|
||||||
$value = $value * $cent_precision;
|
$value = $value * $cent_precision;
|
||||||
return $round ? NumberUtil::round( $value, wc_get_rounding_precision() - wc_get_price_decimals() ) : $value;
|
return $round ? NumberUtil::round( $value, wc_get_rounding_precision() - wc_get_price_decimals() ) : $value;
|
||||||
|
@ -1932,6 +1937,10 @@ function wc_add_number_precision( float $value, bool $round = true ) {
|
||||||
* @return float
|
* @return float
|
||||||
*/
|
*/
|
||||||
function wc_remove_number_precision( $value ) {
|
function wc_remove_number_precision( $value ) {
|
||||||
|
if ( ! $value ) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
$cent_precision = pow( 10, wc_get_price_decimals() );
|
$cent_precision = pow( 10, wc_get_price_decimals() );
|
||||||
return $value / $cent_precision;
|
return $value / $cent_precision;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||||
* Dynamically sets the date column name based on configuration
|
* Dynamically sets the date column name based on configuration
|
||||||
*/
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->date_column_name = get_option( 'woocommerce_date_type', 'date_created' );
|
$this->date_column_name = get_option( 'woocommerce_date_type', 'date_paid' );
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Automattic\WooCommerce\Caches;
|
||||||
|
|
||||||
|
use Automattic\WooCommerce\Caching\ObjectCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to cache order objects.
|
||||||
|
*/
|
||||||
|
class OrderCache extends ObjectCache {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the identifier for the type of the cached objects.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get_object_type(): string {
|
||||||
|
return 'orders';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the id of an object to be cached.
|
||||||
|
*
|
||||||
|
* @param array|object $object The object to be cached.
|
||||||
|
* @return int|string|null The id of the object, or null if it can't be determined.
|
||||||
|
*/
|
||||||
|
protected function get_object_id( $object ) {
|
||||||
|
return $object->get_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate an object before caching it.
|
||||||
|
*
|
||||||
|
* @param array|object $object The object to validate.
|
||||||
|
* @return string[]|null An array of error messages, or null if the object is valid.
|
||||||
|
*/
|
||||||
|
protected function validate( $object ): ?array {
|
||||||
|
if ( ! $object instanceof \WC_Abstract_Order ) {
|
||||||
|
return array( 'The supplied order is not an instance of WC_Abstract_Order, ' . gettype( $object ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue