Merge branch 'trunk' into add/encoding-selector-to-product-importer

This commit is contained in:
Barry Hughes 2023-02-24 08:13:15 -08:00 committed by GitHub
commit 6cd66c5692
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
326 changed files with 5306 additions and 2229 deletions

View File

@ -14,15 +14,13 @@
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 -->
### How to test the changes in this Pull Request:
<!-- Otherwise, please include detailed instructions on how these changes can be tested (including pre-conditions, configuration, steps to take and expected results). It may help to write your instructions using pseudocode -- as if you're telling a computer how to execute the test. -->
<!-- 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)?
1.
2.
@ -35,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 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 included testing instructions?
<!-- Mark completed items with an [x] -->

View File

@ -14,8 +14,8 @@ jobs:
name: Run prepare script
runs-on: ubuntu-20.04
permissions:
contents: read
pull-requests: write
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v3

View File

@ -2,6 +2,10 @@ name: 'Pull request post-merge processing'
on:
pull_request_target:
types: [closed]
paths:
- 'packages/**'
- 'plugins/woocommerce/**'
- 'plugins/woocommerce-admin/**'
permissions: {}

View File

@ -10,8 +10,8 @@ if ( getenv( 'TIME_OVERRIDE' ) ) {
$base_dir = dirname( dirname( dirname( __DIR__ ) ) );
// The release date is 26 days after the code freeze.
$release_time = strtotime( '+26 days', $now );
// The release date is 22 days after the code freeze.
$release_time = strtotime( '+22 days', $now );
$release_date = date( 'Y-m-d', $release_time );
$readme_file = $base_dir . '/plugins/woocommerce/readme.txt';

4
.husky/post-checkout Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
pnpm install

View File

@ -1,6 +1,6 @@
{
"dev": true,
"filter": "^(?:react|react-dom|eslint|typescript|@typescript-eslint|@types/react).*$",
"filter": "^(?:config|react|react-dom|eslint|typescript|@typescript-eslint|@types/react).*$",
"indent": "\t",
"overrides": true,
"peer": true,
@ -33,6 +33,15 @@
"**"
]
},
{
"dependencies": [
"config"
],
"packages": [
"**"
],
"pinVersion": "3.3.7"
},
{
"dependencies": [
"react",

View File

@ -31,8 +31,9 @@ if [ $? -ne 0 ]; then
exit 1
fi
# Ensure both branches are tracked or check-changelogger-use will fail.
git checkout $PROTECTED_BRANCH --quiet
git checkout $CURRENT_BRANCH --quiet
# Ensure both branches are tracked or check-changelogger-use will fail. Note we pass hooksPath
# to avoid running the pre-commit hook.
git -c core.hooksPath=/dev/null checkout $PROTECTED_BRANCH --quiet
git -c core.hooksPath=/dev/null checkout $CURRENT_BRANCH --quiet
php tools/monorepo/check-changelogger-use.php $PROTECTED_BRANCH $CURRENT_BRANCH

View File

@ -1,5 +1,138 @@
== Changelog ==
= 7.4.0 2023-02-14 =
**WooCommerce**
* Fix - Add support for sorting by includes param. [#36215](https://github.com/woocommerce/woocommerce/pull/36215)
* Fix - Allow product tab navigation without prompting for unsaved changes [#36235](https://github.com/woocommerce/woocommerce/pull/36235)
* Fix - Convert HTML to blocks in product variation description [#36241](https://github.com/woocommerce/woocommerce/pull/36241)
* Fix - Decode HTML entities in CategoryBreadcrumbs. [#36321](https://github.com/woocommerce/woocommerce/pull/36321)
* Fix - Decode HTML entities in CategoryFieldItem. [#36367](https://github.com/woocommerce/woocommerce/pull/36367)
* Fix - Ensure order emails are responsive in most email clients, including when the current language is RTL. [#36310](https://github.com/woocommerce/woocommerce/pull/36310)
* Fix - Ensures product variation sort order is correctly persisted. [#36343](https://github.com/woocommerce/woocommerce/pull/36343)
* Fix - Ensure wc_get_order() works without arguments when HPOS is enabled. [#36496](https://github.com/woocommerce/woocommerce/pull/36496)
* Fix - Fix "Save changes?" modal saves the options after selecting the 'Discard' option [#36160](https://github.com/woocommerce/woocommerce/pull/36160)
* Fix - Fix attributes/options lists corrupt render #36236 [#36236](https://github.com/woocommerce/woocommerce/pull/36236)
* Fix - Fix bug when filtering for customer_id=0. [#36216](https://github.com/woocommerce/woocommerce/pull/36216)
* Fix - Fix deprecated usage of ${var} in strings [#36439](https://github.com/woocommerce/woocommerce/pull/36439)
* Fix - Fix edit attribute modal terms list [#36186](https://github.com/woocommerce/woocommerce/pull/36186)
* Fix - Fixes editing of child product reviews. [#35888](https://github.com/woocommerce/woocommerce/pull/35888)
* Fix - Fix for product filters when 'shop' page is the front page. [#36224](https://github.com/woocommerce/woocommerce/pull/36224)
* Fix - Fix issue where attribute term dropdown was not adhering to sort order setting. [#36047](https://github.com/woocommerce/woocommerce/pull/36047)
* Fix - Fix navigation between variations and tab selection [#36239](https://github.com/woocommerce/woocommerce/pull/36239)
* Fix - Fix notices styling in Twenty Twenty-Three [#36475](https://github.com/woocommerce/woocommerce/pull/36475)
* Fix - Fix overlapping header elements on product page [#36495](https://github.com/woocommerce/woocommerce/pull/36495)
* Fix - Fix product table dropdown issue on mobile. [#36046](https://github.com/woocommerce/woocommerce/pull/36046)
* Fix - Fix reordering list items error [#36296](https://github.com/woocommerce/woocommerce/pull/36296)
* Fix - Fix REST API order refunds enpoint when HPOS is active, and make v2 orders endpoint compatible with HPOS [#36308](https://github.com/woocommerce/woocommerce/pull/36308)
* Fix - Fix settings tables styles [#36531](https://github.com/woocommerce/woocommerce/pull/36531)
* Fix - Fix tax task showing as not completed after setting up tax [#36468](https://github.com/woocommerce/woocommerce/pull/36468)
* Fix - Fix the signature mismatch affecting wc cli commands ability to fetch user subscription data. [#36240](https://github.com/woocommerce/woocommerce/pull/36240)
* Fix - Fix total count query of orders within Analytics reports data store. [#35971](https://github.com/woocommerce/woocommerce/pull/35971)
* Fix - Hide Variations section when it is empty [#36202](https://github.com/woocommerce/woocommerce/pull/36202)
* Fix - Improve accessibility of the coupon code label, in the context of the cart page. [#36247](https://github.com/woocommerce/woocommerce/pull/36247)
* Fix - Improve the way we retrieve the alt text property for product attachments. [#35009](https://github.com/woocommerce/woocommerce/pull/35009)
* Fix - Load wc_empty_cart function for REST API calls. [#36182](https://github.com/woocommerce/woocommerce/pull/36182)
* Fix - Make HPOS UX more consistent with posts UI (so that same e2e tests passes for both). [#36282](https://github.com/woocommerce/woocommerce/pull/36282)
* Fix - Make order edit messages compatible with both posts and theorder object. [#36485](https://github.com/woocommerce/woocommerce/pull/36485)
* Fix - Make sure the tracking shortcode only operates in orders with billing information. [#33735](https://github.com/woocommerce/woocommerce/pull/33735)
* Fix - Remove persisted query on return to parent product from variation [#36365](https://github.com/woocommerce/woocommerce/pull/36365)
* Fix - Reset variation form if a new variation is given [#36078](https://github.com/woocommerce/woocommerce/pull/36078)
* Fix - Restore the pre-7.2.0 behavior for single product quantity inputs. [#36460](https://github.com/woocommerce/woocommerce/pull/36460)
* Fix - Set child orders to be children of current order parent before deleting for consistency. [#36218](https://github.com/woocommerce/woocommerce/pull/36218)
* Fix - Skip custom search for HPOS API queries as it's handled already. [#36213](https://github.com/woocommerce/woocommerce/pull/36213)
* Fix - Use Imagick functions to set parallel thread count instead of direct putenv call as suggested in https://core.trac.wordpress.org/ticket/36534#comment:129. [#35339](https://github.com/woocommerce/woocommerce/pull/35339)
* Fix - When adjusting download permissions, confirm the child products have not been removed. [#36431](https://github.com/woocommerce/woocommerce/pull/36431)
* Add - Add ability to filter variations by local attributes in REST API [#36201](https://github.com/woocommerce/woocommerce/pull/36201)
* Add - Add an admin notice about the upcoming PHP version requirement change for PHP 7.2 users [#36444](https://github.com/woocommerce/woocommerce/pull/36444)
* Add - Added a slot for extending the app with a homescreen header banner [#36467](https://github.com/woocommerce/woocommerce/pull/36467)
* Add - Added a slot for ProgressHeader and ProgressTitle component [#36482](https://github.com/woocommerce/woocommerce/pull/36482)
* Add - Add edit button to variations list items [#36079](https://github.com/woocommerce/woocommerce/pull/36079)
* Add - Added slot for tasklist completion slotfill [#36487](https://github.com/woocommerce/woocommerce/pull/36487)
* Add - Add endpoint to create all product variations [#35980](https://github.com/woocommerce/woocommerce/pull/35980)
* Add - Add exit prompt CES for users editing orders when tracking is enabled. [#35762](https://github.com/woocommerce/woocommerce/pull/35762)
* Add - Adding delayed spotlight to feedback button on current product page. [#35865](https://github.com/woocommerce/woocommerce/pull/35865)
* Add - Adding feedback button to activity bar on classic product page. [#35810](https://github.com/woocommerce/woocommerce/pull/35810)
* Add - Adding JS data store for ProductForm. [#36430](https://github.com/woocommerce/woocommerce/pull/36430)
* Add - Adding the WooProductSectionItem slot within the product editor general tab. [#36331](https://github.com/woocommerce/woocommerce/pull/36331)
* Add - Add initial product form PHP helper class to add new fields. [#36093](https://github.com/woocommerce/woocommerce/pull/36093)
* Add - Additional error logging within the CSV Exporter framework. [#34802](https://github.com/woocommerce/woocommerce/pull/34802)
* Add - Add multichannel marketing API [#36453](https://github.com/woocommerce/woocommerce/pull/36453)
* Add - Add new filter to add additional clauses for SQL statement in Variations report [#36378](https://github.com/woocommerce/woocommerce/pull/36378)
* Add - Add new product form API for extending the new Product Form MVP. [#36165](https://github.com/woocommerce/woocommerce/pull/36165)
* Add - Add Options section to new product experience form. [#35910](https://github.com/woocommerce/woocommerce/pull/35910)
* Add - Add product tour to new product management experience [#36428](https://github.com/woocommerce/woocommerce/pull/36428)
* Add - Add product variation form [#36033](https://github.com/woocommerce/woocommerce/pull/36033)
* Add - Add product variation General section [#36081](https://github.com/woocommerce/woocommerce/pull/36081)
* Add - Add product variation header actions and persistence [#36155](https://github.com/woocommerce/woocommerce/pull/36155)
* Add - Add product variation image [#36133](https://github.com/woocommerce/woocommerce/pull/36133)
* Add - Add product variation navigation component [#36076](https://github.com/woocommerce/woocommerce/pull/36076)
* Add - Add product variations flag to only show work in development [#36311](https://github.com/woocommerce/woocommerce/pull/36311)
* Add - Add product variation title to page header [#36085](https://github.com/woocommerce/woocommerce/pull/36085)
* Add - Add Product variation visibility toggle [#36020](https://github.com/woocommerce/woocommerce/pull/36020)
* Add - Add single product variation sections [#36051](https://github.com/woocommerce/woocommerce/pull/36051)
* Add - Adds support for a 'required' argument when invoking `wc_dropdown_variation_attribute_options()`. [#34579](https://github.com/woocommerce/woocommerce/pull/34579)
* Add - Add support for sorting by order metadata in HPOS queries. [#36403](https://github.com/woocommerce/woocommerce/pull/36403)
* Add - Add WooOnboardingTaskListHeader, woocommerce_admin_experimental_onboarding_tasklists filter, and woocommerce_onboarding_task_list_header Slot to task list [#36519](https://github.com/woocommerce/woocommerce/pull/36519)
* Add - Include tax options in pricing section [#36299](https://github.com/woocommerce/woocommerce/pull/36299)
* Add - Persist active tab on refresh [#36112](https://github.com/woocommerce/woocommerce/pull/36112)
* Add - Persist variations order on product save [#36109](https://github.com/woocommerce/woocommerce/pull/36109)
* Add - Product variation quantity status indicator [#35982](https://github.com/woocommerce/woocommerce/pull/35982)
* Add - Product variations card should have a fixed height. [#36053](https://github.com/woocommerce/woocommerce/pull/36053)
* Add - Remove manage_stock 'parent' value before saving the variation [#36234](https://github.com/woocommerce/woocommerce/pull/36234)
* Add - Run ces exit prompt when product import abandoned. [#35996](https://github.com/woocommerce/woocommerce/pull/35996)
* Add - Scroll newly added product attribute into view in new product management experience [#36447](https://github.com/woocommerce/woocommerce/pull/36447)
* Add - Show product CES footer on product tour close [#36516](https://github.com/woocommerce/woocommerce/pull/36516)
* Add - Truncate attribute option name to a max of 32 chars in variations list [#36134](https://github.com/woocommerce/woocommerce/pull/36134)
* Add - Trying experimental slot context with product editor fills. [#36333](https://github.com/woocommerce/woocommerce/pull/36333)
* Add - Using slotfill to insert attributes section in the product editor. [#36483](https://github.com/woocommerce/woocommerce/pull/36483)
* Add - Using slotfill to insert images section in product editor. [#36461](https://github.com/woocommerce/woocommerce/pull/36461)
* Update - Update woocommerce-blocks to 9.4.3. [#36736](https://github.com/woocommerce/woocommerce/pull/36736)
* Update - Adding WooProductFieldItem slot to product details section. [#36315](https://github.com/woocommerce/woocommerce/pull/36315)
* Update - Add permalink_template and generated_slug to products REST API response. [#36497](https://github.com/woocommerce/woocommerce/pull/36497)
* Update - Auto generate variations on option changes [#36188](https://github.com/woocommerce/woocommerce/pull/36188)
* Update - Bundled version of Action Scheduler updated to 3.5.4. [#36433](https://github.com/woocommerce/woocommerce/pull/36433)
* Update - Customers REST API endpoint will now return user metadata only when requester has an administrator role [#36408](https://github.com/woocommerce/woocommerce/pull/36408)
* Update - Disable irrelevant product tabs when variations exist [#35939](https://github.com/woocommerce/woocommerce/pull/35939)
* Update - Migrate shipping section in product editor to slot fill. [#36534](https://github.com/woocommerce/woocommerce/pull/36534)
* Update - Move product management feature flag down to experimental. [#36552](https://github.com/woocommerce/woocommerce/pull/36552)
* Update - Reimplementing product details fields in product editor as slot fills. [#36368](https://github.com/woocommerce/woocommerce/pull/36368)
* Update - Update api-core-tests readme to include a guide for writing tests [#35978](https://github.com/woocommerce/woocommerce/pull/35978)
* Update - Update store-details test snapshot to reflect updated select-control [#35808](https://github.com/woocommerce/woocommerce/pull/35808)
* Update - Update WooCommerce Blocks to 9.4.0 [#36524](https://github.com/woocommerce/woocommerce/pull/36524)
* Update - Update WooCommerce Blocks to 9.4.1 [#36553](https://github.com/woocommerce/woocommerce/pull/36553)
* Update - Update WooCommerce Blocks to 9.4.2 [#36624](https://github.com/woocommerce/woocommerce/pull/36624)
* Dev - Add advanced setting option [#36380](https://github.com/woocommerce/woocommerce/pull/36380)
* Dev - Add experimental SlotFill for task list footer [#36527](https://github.com/woocommerce/woocommerce/pull/36527)
* Dev - Cleanup product task experiment [#35950](https://github.com/woocommerce/woocommerce/pull/35950)
* Dev - Consistent folder structure for E2E and API test results [#35907](https://github.com/woocommerce/woocommerce/pull/35907)
* Dev - Fix docblock type annotations for $meta_value. [#33853](https://github.com/woocommerce/woocommerce/pull/33853)
* Dev - Fix flakiness of the `can save industry changes when navigating back to "Store Details"` E2E test. [#36260](https://github.com/woocommerce/woocommerce/pull/36260)
* Dev - Make shopper tests passable on daily smoke test site. [#35873](https://github.com/woocommerce/woocommerce/pull/35873)
* Dev - Move product attribute fetching logic into a separate hook [#36354](https://github.com/woocommerce/woocommerce/pull/36354)
* Dev - Update TaskLists::add_task() to reflect changes in TaskList::add_task() [#36104](https://github.com/woocommerce/woocommerce/pull/36104)
* Dev - Update the browserslist config for legacy client JS to match Wordpress. [#36264](https://github.com/woocommerce/woocommerce/pull/36264)
* Dev - Upgrade PHPUnit to v8 [#36273](https://github.com/woocommerce/woocommerce/pull/36273)
* Tweak - Corrects a typo in the i18n/states.php file, relating to our list of Iranian states. [#36457](https://github.com/woocommerce/woocommerce/pull/36457)
* Tweak - Derive product type from product attributes [#36243](https://github.com/woocommerce/woocommerce/pull/36243)
* Tweak - Fix typo in a function comment. [#36122](https://github.com/woocommerce/woocommerce/pull/36122)
* Tweak - Fix units in function doc comment [#36353](https://github.com/woocommerce/woocommerce/pull/36353)
* Tweak - Make related products check more robust against wrong transients. [#34742](https://github.com/woocommerce/woocommerce/pull/34742)
* Tweak - Makes it possible to use an `add_meta_boxes_<SCREEN_ID>` style hook in the HPOS editor, for parity with the traditional post editor. [#35999](https://github.com/woocommerce/woocommerce/pull/35999)
* Tweak - Minor adjustments to the ProductForm API [#36414](https://github.com/woocommerce/woocommerce/pull/36414)
* Tweak - Redirect to new product experience when in experiment group [#36381](https://github.com/woocommerce/woocommerce/pull/36381)
* Tweak - Refactor AttributeField into sub-components. [#35997](https://github.com/woocommerce/woocommerce/pull/35997)
* Tweak - Update product links when new product management experience is enabled [#36382](https://github.com/woocommerce/woocommerce/pull/36382)
* Tweak - Updates and improves the docblocks for methods WC_Order::get_total() and WC_Order::get_subtotal(). [#34385](https://github.com/woocommerce/woocommerce/pull/34385)
* Tweak - Validation of Norweigan postcodes has been added. [#36277](https://github.com/woocommerce/woocommerce/pull/36277)
* Performance - Speed up HPOS search query by using group by instead of distinct. [#35897](https://github.com/woocommerce/woocommerce/pull/35897)
* Enhancement - Add context to countries shipping to prefix [#36254](https://github.com/woocommerce/woocommerce/pull/36254)
* Enhancement - Adds new order status filters for bacs and cheque email instructions. [#35849](https://github.com/woocommerce/woocommerce/pull/35849)
* Enhancement - Improves handling of the single product page quantity selector, in relation to variable products. [#36087](https://github.com/woocommerce/woocommerce/pull/36087)
* Enhancement - Remove default WooCommerce button styles if using a block theme which adds button styles in theme.json [#36225](https://github.com/woocommerce/woocommerce/pull/36225)
= 7.3.0 2023-01-10 =
**WooCommerce**

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Unify semver range for `config@3.3.7` (from `^3.3.7`). Fix `node_env_var_name is not defined` error.

View File

@ -29,7 +29,7 @@
"@jest/globals": "^27.5.1",
"@types/jest": "^27.4.1",
"@woocommerce/e2e-utils": "workspace:*",
"config": "^3.3.7"
"config": "3.3.7"
},
"peerDependencies": {
"@woocommerce/e2e-environment": "^0.2.3 || ^0.3.0",

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Added LearnMore option as well as made it possible to use this button multiple instances on the page

View File

@ -0,0 +1,4 @@
Significance: minor
Type: fix
Refactor createOrderedChildren

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Fix issue were Options tab was not showing up anymore in new product management screen.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Fix SelectControl and TreeControl styles.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Add deprecated message to product slot fill components

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Add deprecated message to packages moved to product-editor package.

View File

@ -17,7 +17,7 @@
&:hover,
&:focus-within {
background-color: $gray-0;
background-color: $gray-100;
}
}
&__label {

View File

@ -95,3 +95,9 @@ export {
SlotContextType,
SlotContextHelpersType,
} from './slot-context';
// Exports below can be removed once the @woocommerce/product-editor package is released.
export {
ProductSectionLayout as __experimentalProductSectionLayout,
ProductFieldSection as __experimentalProductFieldSection,
} from './product-section-layout';

View File

@ -9,7 +9,7 @@ import {
useState,
useEffect,
} from '@wordpress/element';
import { SyntheticEvent } from 'react';
import { SyntheticEvent, useCallback } from 'react';
import { useDispatch, useSelect } from '@wordpress/data';
import { PLUGINS_STORE_NAME, InstallPluginsResponse } from '@woocommerce/data';
@ -25,6 +25,11 @@ type PluginsProps = {
pluginSlugs?: string[];
onAbort?: () => void;
abortText?: string;
installText?: string;
installButtonVariant?: Button.BaseProps[ 'variant' ];
learnMoreLink?: string;
learnMoreText?: string;
onLearnMore?: () => void;
};
export const Plugins = ( {
@ -34,10 +39,17 @@ export const Plugins = ( {
onError = () => null,
pluginSlugs = [ 'jetpack', 'woocommerce-services' ],
onSkip,
installText = __( 'Install & enable', 'woocommerce' ),
skipText = __( 'No thanks', 'woocommerce' ),
abortText = __( 'Abort', 'woocommerce' ),
installButtonVariant = 'primary',
learnMoreLink,
learnMoreText = __( 'Learn more', 'woocommerce' ),
onLearnMore,
}: PluginsProps ) => {
const [ hasErrors, setHasErrors ] = useState( false );
// Tracks action so that multiple instances of this button don't all light up when one is clicked
const [ hasBeenClicked, setHasBeenClicked ] = useState( false );
const { installAndActivatePlugins } = useDispatch( PLUGINS_STORE_NAME );
const { isRequesting } = useSelect( ( select ) => {
const { getActivePlugins, getInstalledPlugins, isPluginsRequesting } =
@ -52,48 +64,56 @@ export const Plugins = ( {
};
} );
const handleErrors = (
errors: unknown,
response: InstallPluginsResponse
) => {
setHasErrors( true );
const handleErrors = useCallback(
( errors: unknown, response: InstallPluginsResponse ) => {
setHasErrors( true );
onError( errors, response );
};
onError( errors, response );
},
[ onError ]
);
const handleSuccess = (
plugins: string[],
response: InstallPluginsResponse
) => {
onComplete( plugins, response );
};
const handleSuccess = useCallback(
( plugins: string[], response: InstallPluginsResponse ) => {
onComplete( plugins, response );
},
[ onComplete ]
);
const installAndActivate = async (
event?: SyntheticEvent< HTMLAnchorElement >
) => {
if ( event ) {
event.preventDefault();
}
const installAndActivate = useCallback(
async ( event?: SyntheticEvent< HTMLAnchorElement > ) => {
if ( event ) {
event.preventDefault();
}
// Avoid double activating.
if ( isRequesting ) {
return false;
}
// Avoid double activating.
if ( isRequesting ) {
return false;
}
installAndActivatePlugins( pluginSlugs )
.then( ( response ) => {
handleSuccess( response.data.activated, response );
} )
.catch( ( response ) => {
handleErrors( response.errors, response );
} );
};
installAndActivatePlugins( pluginSlugs )
.then( ( response ) => {
handleSuccess( response.data.activated, response );
} )
.catch( ( response ) => {
setHasBeenClicked( false );
handleErrors( response.errors, response );
} );
},
[
handleErrors,
handleSuccess,
installAndActivatePlugins,
isRequesting,
pluginSlugs,
]
);
useEffect( () => {
if ( autoInstall ) {
installAndActivate();
}
}, [] );
}, [ autoInstall, installAndActivate ] );
if ( hasErrors ) {
return (
@ -131,17 +151,32 @@ export const Plugins = ( {
return (
<>
<Button
isBusy={ isRequesting }
isPrimary
onClick={ installAndActivate }
isBusy={ isRequesting && hasBeenClicked }
variant={
isRequesting && hasBeenClicked
? 'primary' // set to primary when busy, the other variants look weird when combined with isBusy
: installButtonVariant
}
disabled={ isRequesting && hasBeenClicked }
onClick={ () => {
setHasBeenClicked( true );
installAndActivate();
} }
>
{ __( 'Install & enable', 'woocommerce' ) }
{ installText }
</Button>
{ onSkip && (
<Button isTertiary onClick={ onSkip }>
{ skipText }
</Button>
) }
{ learnMoreLink && (
<a href={ learnMoreLink } target="_blank" rel="noreferrer">
<Button isTertiary onClick={ onLearnMore }>
{ learnMoreText }
</Button>
</a>
) }
{ onAbort && (
<Button isTertiary onClick={ onAbort }>
{ abortText }

View File

@ -0,0 +1,47 @@
/**
* External dependencies
*/
import { createElement } from '@wordpress/element';
import { Card, CardBody } from '@wordpress/components';
import deprecated from '@wordpress/deprecated';
/**
* Internal dependencies
*/
import { ProductSectionLayout } from './product-section-layout';
import { WooProductFieldItem } from '../woo-product-field-item';
type ProductFieldSectionProps = {
id: string;
title: string;
description: string | JSX.Element;
className?: string;
};
export const ProductFieldSection: React.FC< ProductFieldSectionProps > = ( {
id,
title,
description,
className,
children,
} ) => {
deprecated( `__experimentalProductFieldSection`, {
version: '13.0.0',
plugin: '@woocommerce/components',
hint: 'Moved to @woocommerce/product-editor package: import { __experimentalProductFieldSection } from @woocommerce/product-editor',
} );
return (
<ProductSectionLayout
title={ title }
description={ description }
className={ className }
>
<Card>
<CardBody>
{ children }
<WooProductFieldItem.Slot section={ id } />
</CardBody>
</Card>
</ProductSectionLayout>
);
};

View File

@ -0,0 +1,44 @@
/**
* External dependencies
*/
import { Children, isValidElement, createElement } from '@wordpress/element';
import deprecated from '@wordpress/deprecated';
/**
* Internal dependencies
*/
import { FormSection } from '../form-section';
type ProductSectionLayoutProps = {
title: string;
description: string | JSX.Element;
className?: string;
};
export const ProductSectionLayout: React.FC< ProductSectionLayoutProps > = ( {
title,
description,
className,
children,
} ) => {
deprecated( `__experimentalProductSectionLayout`, {
version: '13.0.0',
plugin: '@woocommerce/components',
hint: 'Moved to @woocommerce/product-editor package: import { __experimentalProductSectionLayout } from @woocommerce/product-editor',
} );
return (
<FormSection
title={ title }
description={ description }
className={ className }
>
{ Children.map( children, ( child ) => {
if ( isValidElement( child ) && child.props.onChange ) {
return (
<div className="product-field-layout">{ child }</div>
);
}
return child;
} ) }
</FormSection>
);
};

View File

@ -1,7 +1,6 @@
/**
* External Dependencies
*/
@import 'node_modules/@wordpress/base-styles/colors.native';
@import '@automattic/tour-kit/dist/esm/styles.scss';
/**
@ -57,3 +56,4 @@
@import 'collapsible-content/style.scss';
@import 'form/style.scss';
@import 'experimental-tree-control/tree.scss';
@import 'product-section-layout/style.scss';

View File

@ -5,12 +5,58 @@ 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 {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 > >(
@ -19,15 +65,9 @@ function createOrderedChildren< T = Fill.Props, S = Record< string, unknown > >(
props: T,
injectProps?: S
) {
if ( typeof children === 'function' ) {
return cloneElement( children( { ...props, order, ...injectProps } ), {
order,
...injectProps,
} );
} else if ( isValidElement( children ) ) {
return cloneElement( children, { ...props, order, ...injectProps } );
}
throw Error( 'Invalid children type' );
const { children: childrenToRender, props: propsToRender } =
getChildrenAndProps( children, order, props, injectProps );
return cloneElement( childrenToRender, propsToRender );
}
export { createOrderedChildren };

View File

@ -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:
| Prop | Type | Description |
| -------------| -------- | ------------------------------------------------------------------------------------------------------------------------ |
| `id` | String | A unique string to identify your fill. Used for configuiration management. |
| `section ` | String | The string used to identify the particular section where you want to render your field. |
| `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. |
| 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, 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 |
| ----------- | ------ | ---------------------------------------------------------------------------------------------------- |
| `section` | String | Unique to the section that the Slot appears, and must be the same as the one provided to any fills. |
| 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. |

View File

@ -4,6 +4,7 @@
import React, { useEffect } from 'react';
import { Slot, Fill } from '@wordpress/components';
import { createElement, Children, Fragment } from '@wordpress/element';
import deprecated from '@wordpress/deprecated';
/**
* Internal dependencies
@ -29,6 +30,8 @@ type WooProductFieldFillProps = {
children?: React.ReactNode;
};
const DEFAULT_FIELD_ORDER = 20;
const WooProductFieldFill: React.FC< WooProductFieldFillProps > = ( {
fieldName,
sectionName,
@ -39,6 +42,12 @@ const WooProductFieldFill: React.FC< WooProductFieldFillProps > = ( {
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( () => {
registerFill( fieldId );
}, [] );
@ -75,16 +84,18 @@ export const WooProductFieldItem: React.FC< WooProductFieldItemProps > & {
} = ( { children, sections, id } ) => {
return (
<>
{ sections.map( ( { name: sectionName, order = 20 } ) => (
<WooProductFieldFill
fieldName={ id }
sectionName={ sectionName }
order={ order }
key={ sectionName }
>
{ children }
</WooProductFieldFill>
) ) }
{ sections.map(
( { name: sectionName, order = DEFAULT_FIELD_ORDER } ) => (
<WooProductFieldFill
fieldName={ id }
sectionName={ sectionName }
order={ order }
key={ sectionName }
>
{ children }
</WooProductFieldFill>
)
) }
</>
);
};

View File

@ -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:
| Prop | Type | Description |
| -------------| -------- | ------------------------------------------------------------------------------------------------------------------------ |
| `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. |
| `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. |
| Prop | Type | Description |
| ---------- | ------ | -------------------------------------------------------------------------------------------------- |
| `id` | String | A unique string to identify your fill. Used for configuiration 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, 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 |
| ----------- | ------ | ---------------------------------------------------------------------------------------------------- |
| `location` | String | Unique to the location that the Slot appears, and must be the same as the one provided to any fills. |
| 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. |

View File

@ -4,6 +4,7 @@
import React from 'react';
import { Slot, Fill } from '@wordpress/components';
import { createElement, Fragment } from '@wordpress/element';
import deprecated from '@wordpress/deprecated';
/**
* Internal dependencies
@ -21,12 +22,20 @@ type WooProductSectionSlotProps = {
tab: string;
};
const DEFAULT_SECTION_ORDER = 20;
export const WooProductSectionItem: React.FC< WooProductSectionItemProps > & {
Slot: React.FC< Slot.Props & WooProductSectionSlotProps >;
} = ( { 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 (
<>
{ tabs.map( ( { name: tabName, order: tabOrder } ) => (
{ tabs.map( ( { name: tabName, order: sectionOrder } ) => (
<Fill
name={ `woocommerce_product_section_${ tabName }` }
key={ tabName }
@ -34,7 +43,7 @@ export const WooProductSectionItem: React.FC< WooProductSectionItemProps > & {
{ ( fillProps: Fill.Props ) => {
return createOrderedChildren<
Fill.Props & { tabName: string }
>( children, tabOrder || 20, {
>( children, sectionOrder || DEFAULT_SECTION_ORDER, {
tabName,
...fillProps,
} );

View File

@ -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:
| Prop | Type | Description |
| ---------- | ------ | -------------------------------------------------------------------------------------------------------------- |
| `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. |
| `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. |
| Prop | Type | Description |
| ----------- | ------ | -------------------------------------------------------------------------------------------------------------- |
| `id` | String | A unique string to identify your fill. Used for configuiration 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, 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 |
| ---------- | ------ | ---------------------------------------------------------------------------------------------------- |
| `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. |

View File

@ -4,6 +4,7 @@
import React, { ReactElement, ReactNode } from 'react';
import { Slot, Fill, TabPanel } from '@wordpress/components';
import { createElement, Fragment } from '@wordpress/element';
import deprecated from '@wordpress/deprecated';
/**
* Internal dependencies
@ -30,11 +31,19 @@ type WooProductFieldSlotProps = {
) => ReactElement | null;
};
const DEFAULT_TAB_ORDER = 20;
export const WooProductTabItem: React.FC< WooProductTabItemProps > & {
Slot: React.VFC<
Omit< Slot.Props, 'children' > & WooProductFieldSlotProps
>;
} = ( { 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 ) {
// eslint-disable-next-line no-console
console.warn( 'WooProductTabItem fill is missing templates property.' );
@ -50,12 +59,12 @@ export const WooProductTabItem: React.FC< WooProductTabItemProps > & {
{ ( fillProps: Fill.Props ) => {
return createOrderedChildren< Fill.Props >(
children,
templateData.order || 20,
templateData.order || DEFAULT_TAB_ORDER,
{},
{
tabProps,
templateName: templateData.name,
order: templateData.order || 20,
order: templateData.order || DEFAULT_TAB_ORDER,
...fillProps,
}
);
@ -84,7 +93,7 @@ WooProductTabItem.Slot = ( { fillProps, template, children } ) => (
: props.tabProps;
tabs.push( {
...tabProps,
order: props.order ?? 20,
order: props.order ?? DEFAULT_TAB_ORDER,
} );
}
return {

View File

@ -2,6 +2,12 @@
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.2](https://www.npmjs.com/package/@woocommerce/create-woo-extension/v/1.0.2) - 2023-02-13
- Patch - Add examples of Woo components package [#36732]
- Patch - bump WooCommerce version [#36732]
- Patch - Update readme [#36732]
## [1.0.1](https://www.npmjs.com/package/@woocommerce/create-woo-extension/v/1.0.1) - 2022-12-20
- Patch - Fix install scripts [#34385]

View File

@ -1,4 +0,0 @@
Significance: patch
Type: add
Add examples of Woo components package

View File

@ -1,3 +0,0 @@
Significance: patch
Type: dev
Comment: Just some PHP clean up to adhere to coding standards

View File

@ -1,5 +0,0 @@
Significance: patch
Type: fix
Comment: Dev dependency update.

View File

@ -1,4 +0,0 @@
Significance: patch
Type: dev
Update readme

View File

@ -1,4 +0,0 @@
Significance: patch
Type: update
bump WooCommerce version

View File

@ -1,6 +1,6 @@
{
"name": "@woocommerce/create-woo-extension",
"version": "1.0.1",
"version": "1.0.2",
"description": "A template to be used with `@wordpress/create-block` to create a WooCommerce extension.",
"main": "index.js",
"engines": {

View File

@ -0,0 +1,4 @@
Significance: minor
Type: fix
Moved setIsRequesting(false) to the finally block so that it always runs even if an exception is thrown.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: performance
Use createSelector for getPermalinkParts selector, to cache result.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: update
Use products data store for getPermalinkParts selector.

View File

@ -86,7 +86,7 @@
"build": "pnpm -w exec turbo run turbo:build --filter=$npm_package_name",
"test": "pnpm -w exec turbo run turbo:test --filter=$npm_package_name",
"lint": "eslint src",
"start": "tsc --build --watch",
"start": "tsc --build --watch ./tsconfig.json ./tsconfig-cjs.json",
"prepack": "pnpm run clean && pnpm run build",
"lint:fix": "eslint src --fix",
"test-staged": "jest --bail --config ./jest.config.json --findRelatedTests"

View File

@ -223,10 +223,11 @@ export function* installPlugins( plugins: Partial< PluginNames >[] ) {
throw results.errors.errors;
}
yield setIsRequesting( 'installPlugins', false );
return results;
} catch ( error ) {
yield handlePluginAPIError( 'install', plugins, error );
} finally {
yield setIsRequesting( 'installPlugins', false );
}
}
@ -248,11 +249,11 @@ export function* activatePlugins( plugins: Partial< PluginNames >[] ) {
throw results.errors.errors;
}
yield setIsRequesting( 'activatePlugins', false );
return results;
} catch ( error ) {
yield handlePluginAPIError( 'activate', plugins, error );
} finally {
yield setIsRequesting( 'activatePlugins', false );
}
}

View File

@ -19,7 +19,6 @@ registerStore< State >( STORE_NAME, {
reducer: reducer as Reducer< ProductState >,
actions,
controls,
// @ts-expect-error as the registerStore type is not allowing the createRegistrySelector selector.
selectors,
resolvers,
} );

View File

@ -2,12 +2,17 @@
* External dependencies
*/
import { addQueryArgs } from '@wordpress/url';
import { apiFetch } from '@wordpress/data-controls';
import {
apiFetch,
dispatch as deprecatedDispatch,
select,
} from '@wordpress/data-controls';
import { controls } from '@wordpress/data';
/**
* Internal dependencies
*/
import { WC_PRODUCT_NAMESPACE } from './constants';
import { STORE_NAME, WC_PRODUCT_NAMESPACE } from './constants';
import { Product, ProductQuery } from './types';
import {
getProductError,
@ -19,6 +24,11 @@ import {
} from './actions';
import { request } from '../utils';
const dispatch =
controls && controls.dispatch ? controls.dispatch : deprecatedDispatch;
const resolveSelect =
controls && controls.resolveSelect ? controls.resolveSelect : select;
export function* getProducts( query: Partial< ProductQuery > ) {
// id is always required.
const productsQuery = {
@ -56,6 +66,11 @@ export function* getProduct( productId: number ) {
} );
yield getProductSuccess( productId, product );
yield dispatch( STORE_NAME, 'finishResolution', 'getPermalinkParts', [
productId,
] );
return product;
} catch ( error ) {
yield getProductError( productId, error );
@ -81,3 +96,7 @@ export function* getProductsTotalCount( query: Partial< ProductQuery > ) {
throw error;
}
}
export function* getPermalinkParts( productId: number ) {
yield resolveSelect( STORE_NAME, 'getProduct', [ productId ] );
}

View File

@ -2,7 +2,6 @@
* External dependencies
*/
import createSelector from 'rememo';
import { createRegistrySelector } from '@wordpress/data';
/**
* Internal dependencies
@ -122,22 +121,10 @@ export const isPending = (
return false;
};
export const getPermalinkParts = createRegistrySelector(
( select ) => ( state: ProductState, productId: number ) => {
const product = select( 'core' ).getEntityRecord(
'postType',
'product',
productId,
// @ts-expect-error query object is not part of the @wordpress/core-data types yet.
{
_fields: [
'id',
'permalink_template',
'slug',
'generated_slug',
],
}
);
export const getPermalinkParts = createSelector(
( state: ProductState, productId: number ) => {
const product = state.data[ productId ];
if ( product && product.permalink_template ) {
const postName = product.slug || product.generated_slug;
@ -152,6 +139,9 @@ export const getPermalinkParts = createRegistrySelector(
};
}
return null;
},
( state, productId ) => {
return [ state.data[ productId ] ];
}
);
@ -162,7 +152,5 @@ export type ProductsSelectors = {
getProductsTotalCount: WPDataSelector< typeof getProductsTotalCount >;
getProductsError: WPDataSelector< typeof getProductsError >;
isPending: WPDataSelector< typeof isPending >;
getPermalinkParts: (
productId: number
) => { prefix: string; postName: string; suffix: string } | null;
getPermalinkParts: WPDataSelector< typeof getPermalinkParts >;
} & WPDataSelectors;

View File

@ -66,6 +66,7 @@ export type Product< Status = ProductStatus, Type = ProductType > = Omit<
downloads: ProductDownload[];
external_url: string;
featured: boolean;
generated_slug: string;
id: number;
low_stock_amount: number;
manage_stock: boolean;
@ -73,6 +74,7 @@ export type Product< Status = ProductStatus, Type = ProductType > = Omit<
name: string;
on_sale: boolean;
permalink: string;
permalink_template: string;
price: string;
price_html: string;
purchasable: boolean;
@ -108,9 +110,11 @@ export const productReadOnlyProperties = [
'date_created_gmt',
'date_modified',
'date_modified_gmt',
'generated_slug',
'id',
'on_sale',
'permalink',
'permalink_template',
'price',
'price_html',
'purchasable',

View File

@ -25,7 +25,7 @@
"dependencies": {
"@jest/globals": "^27.5.1",
"@wordpress/deprecated": "^3.2.3",
"config": "3.3.3"
"config": "3.3.7"
},
"devDependencies": {
"@babel/cli": "7.12.8",

View File

@ -33,7 +33,7 @@
"@wordpress/jest-preset-default": "^7.1.3",
"app-root-path": "^3.0.0",
"commander": "4.1.1",
"config": "3.3.3",
"config": "3.3.7",
"jest": "^27.5.1",
"jest-circus": "27.5.1",
"jest-each": "27.5.1",

View File

@ -18,7 +18,7 @@
"@automattic/puppeteer-utils": "github:Automattic/puppeteer-utils#0f3ec50",
"@wordpress/deprecated": "^3.2.3",
"@wordpress/e2e-test-utils": "wp-5.8",
"config": "3.3.3",
"config": "3.3.7",
"fishery": "^1.2.0"
},
"devDependencies": {

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Fix missing fills prop in useSlotFills return object for wp.components >= 21.2.0

View File

@ -0,0 +1,5 @@
Significance: patch
Type: update
Support direction prop to control which direction hidden items open.

View File

@ -32,6 +32,7 @@ type CollapsibleListProps = {
show?: number;
onCollapse?: () => void;
onExpand?: () => void;
direction?: 'up' | 'down';
} & ListProps;
const defaultStyle = {
@ -126,6 +127,7 @@ export const ExperimentalCollapsibleList: React.FC< CollapsibleListProps > = ( {
show = 0,
onCollapse,
onExpand,
direction = 'up',
...listProps
} ): JSX.Element => {
const [ isCollapsed, setCollapsed ] = useState( collapsed );
@ -225,9 +227,33 @@ export const ExperimentalCollapsibleList: React.FC< CollapsibleListProps > = ( {
'woocommerce-experimental-list-wrapper': ! isCollapsed,
} );
const hiddenChildren =
displayedChildren.hidden.length > 0 ? (
<ExperimentalListItem
key="collapse-item"
className="list-item-collapse"
onClick={ clickHandler }
animation="none"
disableGutters
>
<p>
{ isCollapsed
? footerLabels.expand
: footerLabels.collapse }
</p>
<Icon
className="list-item-collapse__icon"
size={ 30 }
icon={ isCollapsed ? chevronDown : chevronUp }
/>
</ExperimentalListItem>
) : null;
return (
<ExperimentalList { ...listProps } className={ listClasses }>
{ [
direction === 'down' && hiddenChildren,
...displayedChildren.shown,
<Transition
key="remaining-children"
@ -288,27 +314,7 @@ export const ExperimentalCollapsibleList: React.FC< CollapsibleListProps > = ( {
);
} }
</Transition>,
displayedChildren.hidden.length > 0 ? (
<ExperimentalListItem
key="collapse-item"
className="list-item-collapse"
onClick={ clickHandler }
animation="none"
disableGutters
>
<p>
{ isCollapsed
? footerLabels.expand
: footerLabels.collapse }
</p>
<Icon
className="list-item-collapse__icon"
size={ 30 }
icon={ isCollapsed ? chevronDown : chevronUp }
/>
</ExperimentalListItem>
) : null,
direction === 'up' && hiddenChildren,
] }
</ExperimentalList>
);

View File

@ -16,6 +16,14 @@ export default {
title: 'WooCommerce Admin/experimental/List',
component: List,
decorators: [ ( storyFn, context ) => withConsole()( storyFn )( context ) ],
argTypes: {
direction: {
control: {
type: 'select',
options: [ 'up', 'down' ],
},
},
},
} as Meta;
const Template: Story< ListProps > = ( args ) => (
@ -40,9 +48,10 @@ export const Primary = Template.bind( { onClick: () => {} } );
Primary.args = {
listType: 'ul',
animation: 'slide-right',
direction: 'top',
};
export const CollapsibleListExample: Story = () => {
export const CollapsibleListExample: Story = ( args ) => {
return (
<CollapsibleList
collapseLabel="Show less"
@ -56,6 +65,8 @@ export const CollapsibleListExample: Story = () => {
// eslint-disable-next-line no-console
console.log( 'expanded' );
} }
direction="top"
{ ...args }
>
<ListItem onClick={ () => {} }>
<div>Any markup can go here.</div>

View File

@ -9,6 +9,7 @@ import {
__experimentalNavigationItem,
__experimentalText,
__experimentalUseSlot,
__experimentalUseSlotFills as useSlotFillsHook,
Navigation as NavigationComponent,
NavigationBackButton as NavigationBackButtonComponent,
NavigationGroup as NavigationGroupComponent,
@ -31,7 +32,28 @@ export const NavigationMenu =
export const NavigationItem =
NavigationItemComponent || __experimentalNavigationItem;
export const Text = TextComponent || __experimentalText;
export const useSlot = useSlotHook || __experimentalUseSlot;
// Add a fallback for useSlotFills hook to not break in older versions of wp.components.
// This hook was introduced in wp.components@21.2.0.
const useSlotFills = useSlotFillsHook || ( () => null );
export const useSlot = ( name ) => {
const _useSlot = useSlotHook || __experimentalUseSlot;
const slot = _useSlot( name );
const fills = useSlotFills( name );
/*
* Since wp.components@21.2.0, the slot object no longer contains the fills prop.
* Add fills prop to the slot object for backward compatibility.
*/
if ( typeof useSlotFillsHook === 'function' ) {
return {
...slot,
fills,
};
}
return slot;
};
export { ExperimentalListItem as ListItem } from './experimental-list/experimental-list-item';
export { ExperimentalList as List } from './experimental-list/experimental-list';

View File

@ -2,12 +2,24 @@
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.2.0](https://www.npmjs.com/package/@woocommerce/packages/js/onboarding/v/3.2.0) - 2022-07-08
## [3.3.0](https://www.npmjs.com/package/@woocommerce/onboarding/v/3.3.0) - 2023-02-14
- Patch - Added in missing TS definitions in package.json [#36701]
- Patch - Fix wcpay benefits padding [#36701]
- Minor - Add WooOnboardingTaskListHeader component [#36701]
- Minor - Adjust build/test scripts to remove -- -- that was required for pnpm 6. [#36701]
- Patch - Cleanup product task experiment [#36701]
- Minor - Fix node and pnpm versions via engines [#36701]
- Minor - Match TypeScript version with syncpack [#36701]
- Patch - Update eslint to 8.32.0 across the monorepo. [#36701]
- Minor - Update pnpm version constraint to 7.13.3 to avoid auto-install-peers issues [#36701]
## [3.2.0](https://www.npmjs.com/package/@woocommerce/onboarding/v/3.2.0) - 2022-07-08
- Minor - Add WCPayBanner & WCPayBenefits components
- Minor - Remove PHP and Composer dependencies for packaged JS packages
## [3.1.0](https://www.npmjs.com/package/@woocommerce/packages/js/onboarding/v/3.1.0) - 2022-06-15
## [3.1.0](https://www.npmjs.com/package/@woocommerce/onboarding/v/3.1.0) - 2022-06-15
- Minor - Add ExPlat dependency and product task experiment logic
- Minor - Add Jetpack Changelogger

View File

@ -1,4 +0,0 @@
Significance: minor
Type: add
Add WooOnboardingTaskListHeader component

View File

@ -1,4 +0,0 @@
Significance: minor
Type: dev
Update pnpm version constraint to 7.13.3 to avoid auto-install-peers issues

View File

@ -1,4 +0,0 @@
Significance: patch
Type: dev
Cleanup product task experiment

View File

@ -1,4 +0,0 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -1,4 +0,0 @@
Significance: minor
Type: dev
Fix node and pnpm versions via engines

View File

@ -1,5 +0,0 @@
Significance: patch
Type: dev
Comment: Package scripts were modified to support simplified running of turbo commands in the monorepo.

View File

@ -1,4 +0,0 @@
Significance: patch
Type: fix
Fix wcpay benefits padding

View File

@ -1,4 +0,0 @@
Significance: patch
Type: fix
Added in missing TS definitions in package.json

View File

@ -1,4 +0,0 @@
Significance: minor
Type: dev
Match TypeScript version with syncpack

View File

@ -1,5 +0,0 @@
Significance: patch
Type: fix
Comment: Dev dependency update.

View File

@ -1,4 +0,0 @@
Significance: minor
Type: dev
Adjust build/test scripts to remove -- -- that was required for pnpm 6.

View File

@ -1,6 +1,6 @@
{
"name": "@woocommerce/onboarding",
"version": "3.2.0",
"version": "3.3.0",
"description": "Onboarding utilities.",
"author": "Automattic",
"license": "GPL-3.0-or-later",

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Copy over the product slot fill components from @woocommerce/components

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add product editor utils

View File

@ -29,19 +29,28 @@
},
"dependencies": {
"@woocommerce/components": "workspace:*",
"@woocommerce/data": "workspace:^4.1.0",
"@woocommerce/tracks": "workspace:^1.3.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": {
"@types/react": "^17.0.2",
"@types/wordpress__components": "^19.10.1",
"@woocommerce/eslint-plugin": "workspace:*",
"@woocommerce/internal-style-build": "workspace:*",
"@wordpress/browserslist-config": "^4.1.1",
"concurrently": "^7.0.0",
"css-loader": "^3.6.0",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"postcss-loader": "^3.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rimraf": "^3.0.2",
"sass-loader": "^10.2.1",
"ts-jest": "^27.1.3",
"typescript": "^4.8.3",
@ -60,5 +69,9 @@
"start": "concurrently \"tsc --build --watch\" \"webpack --watch\"",
"prepack": "pnpm run clean && pnpm run build",
"lint:fix": "eslint src --fix"
},
"peerDependencies": {
"@types/react": "^17.0.2",
"react": "^17.0.2"
}
}

View File

@ -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';

View File

@ -0,0 +1,2 @@
export * from './product-section-layout';
export * from './product-field-section';

View File

@ -0,0 +1,52 @@
.woocommerce-form-section {
a {
text-decoration: none;
}
&__content {
.components-card {
border: 1px solid $gray-400;
border-radius: 2px;
box-shadow: none;
&__body {
padding: $gap-large;
.components-base-control,
.components-dropdown,
.woocommerce-rich-text-editor {
&:not(:first-child):not(.components-radio-control) {
margin-top: $gap-large - $gap-smaller;
margin-bottom: 0;
}
}
}
}
.woocommerce-product-form__field:not(:first-child) {
margin-top: $gap-large;
> .components-base-control {
margin-bottom: 0;
}
}
.components-radio-control .components-v-stack {
gap: $gap-small;
}
.woocommerce-collapsible-content {
margin-top: $gap-large;
}
}
&__header {
p > span {
display: block;
margin-bottom: $gap-smaller;
}
}
&:not(:first-child) {
margin-top: $gap-largest;
}
}

View File

@ -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. |

View File

@ -0,0 +1 @@
export * from './woo-product-field-item';

View File

@ -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>
);
};

View File

@ -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. |

View File

@ -0,0 +1 @@
export * from './woo-product-section-item';

View File

@ -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>
);

View File

@ -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. |

View File

@ -0,0 +1 @@
export * from './woo-product-tab-item';

View File

@ -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>
);

View File

@ -1,4 +1,6 @@
export {
ProductSectionLayout as __experimentalProductSectionLayout,
ProductFieldSection as __experimentalProductFieldSection,
} from './product-section-layout';
export * from './components';
/**
* Utils
*/
export * from './utils';

View File

@ -1 +1 @@
@import 'product-section-layout/style.scss';
@import 'components/product-section-layout/style.scss';

View File

@ -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';

View File

@ -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 };

View File

@ -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, '' );
};

View File

@ -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,
} );
},
};
}

View File

@ -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,
};
};

View File

@ -6,7 +6,7 @@ import { ProductVariation } from '@woocommerce/data';
/**
* 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.

View File

@ -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,
};

View File

@ -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>;
};

Some files were not shown because too many files have changed in this diff Show More